In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-11 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article introduces the relevant knowledge of "how Vue.js templates and data are rendered into DOM". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Preface
One of the core ideas of Vue.js is data driven. In other words, the view is data-driven, and our changes to the view do not directly manipulate the DOM, but by modifying the data. When the interaction is complex, only care about the modification of the data will make the logic of the code very clear, because the DOM becomes the mapping of the data, and all our logic is to modify the data without touching the DOM. This kind of code is very easy to maintain.
In Vue.js, we can use concise template syntax to declaratively render the data as DOM:
{{msg}} var app = new Vue ({el:'# app', data: {msg: 'Hello worldview'}})
Hello world! is displayed on the results page. This is the knowledge you know when you get started with vue.js. So now we have to ask what the source code of vue.js has done, so that the template and data can finally be rendered as DOM???.
Start with new Vue ()
When writing a vue project, a vue is instantiated in the project's entry file main.js file. As follows:
Var app = new Vue ({el:'# app', data: {msg: 'Hello worldview'},})
Vue is a class implemented in Function. The source code is as follows: in src/core/instance/index.js
The location of the / / _ init method import {initMixin} from'. / init' / / Vue is a class implemented in Function, so it is instantiated through new Vue. Function Vue (options) {if (process.env.NODE_ENV! = = 'production' & &! (this instanceof Vue)) {warn (' Vue is a constructor and should be called with the `new` keyword')} this._init (options)}
When we pass an object into new Vue ({}) in the project, we actually execute the above method, pass in the parameter options, and then call the this._init (options) method. The method is in the src/core/instance/init.js file. The code is as follows:
Import {initState} from'. / state' Vue.prototype._init = function (options?: Object) {const vm: Component = this / / defines uid vm._uid = uid++ let startTag EndTag if (process.env.NODE_ENV! = = 'production' & & config.performance & & mark) {startTag = `vue-perf-start:$ {vm._uid} `endTag = `vue-perf-end:$ {vm._uid}` mark (startTag)} vm._isVue = true / / merge options if (options & & options._isComponent) {initInternalComponent (vm) Options)} else {/ / here all the incoming options is merged on $options. / / so we can access el in new Vue in vue project through $el / / access data vm.$options = mergeOptions (resolveConstructorOptions (vm.constructor), options) in new Vue in vue project through $options.data. | {} Vm)} if (process.env.NODE_ENV! = = 'production') {initProxy (vm)} else {vm._renderProxy = vm} / / initialization function vm._self = vm initLifecycle (vm) / / Lifecycle function initEvents (vm) / / initialization event chain initRender (vm) callHook (vm) 'beforeCreate') initInjections (vm) / / resolve injections before data/props initState (vm) initProvide (vm) / / resolve provide after data/props callHook (vm,' created') if (process.env.NODE_ENV! = 'production' & & config.performance & & mark) {vm._name = formatComponentName (vm, false) mark (endTag) measure (`vue ${vm._name} init`, startTag EndTag)} / / determine whether the current $options.el has el, that is, whether to pass in the mounted DOM object if (vm.$options.el) {vm.$mount (vm.$options.el)}}
From the above code, we can see that this._init (options) mainly merges configuration, initializes lifecycle, initializes event center, initializes rendering, initializes data, props, computed, watcher, and so on. The important parts are annotated in the code.
Then let's take one of the functions as an example: take initState (vm) as an example:
Why can you access the data defined in data in the hook function?
In the vue project, when data is defined, the properties defined in data can be accessed either in the component's hook function or in the methods function. Why is that?
Var app = new Vue ({el:'# app', data: () {return {msg: 'Hello worldview'}}, mounted () {console.log (this.msg) / / logs' Hello worldview'}
Analyze the source code: you can see the this._init (options) method, and there is an initState (vm) function in the initialization function section. This method is in. / state.js: the specific code is as follows:
Export function initState (vm: Component) {vm._watchers = [] const opts = vm.$options / / initialize props; if (opts.props) initProps (vm, opts.props) if props is defined / / initialize methods if methods is defined If (opts.methods) initMethods (vm, opts.methods) if (opts.data) {/ / initialize data if data is defined (content to be analyzed starts here) initData (vm)} else {observe (vm._data = {}, true / * asRootData * /)} if (opts.computed) initComputed (vm, opts.computed) if (opts.watch & & opts.watch! = nativeWatch) {initWatch (vm, opts.watch)}}
Judge in the initState method: if data is defined, initialize data; and continue to look at the function that initializes data: initData (vm). The code is as follows:
Function initData (vm: Component) {/ * this data is the data defined in our vue project. That is, data () {return {msg: 'Hello worldview'} * / let data = vm.$options.data / / after getting the data in the above example, determine whether it is a function data = vm._data = typeof data = = 'function'? GetData (data, vm) / / if the getData () method is executed This method returns data: data | {} / / if it is not an object, report a warning if (! isPlainObject (data)) {data = {} process.env.NODE_ENV! = 'production' & & warn (' data functions should return an object:\ n' + 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',) in the development environment Vm)} / / get the attribute const keys = Object.keys (data) defined by data / / get props const props = vm.$options.props / / get methods const methods = vm.$options.methods let I = keys.length / / do a loop comparison If an attribute is defined on data, it cannot be defined in props and methods. Because whether it is defined in data, defined in props, or defined in medthods, it is eventually mounted on the vm instance. See proxy (vm, `_ data`, key) while (iMury -) {const key = keys [I] if (process.env.NODE_ENV! = = 'production') {if (methods & & hasOwn (methods, key)) {warn (`Method "${key}" has already been defined as a data property.`, vm)} if (props & hasOwn (props)) Key) {process.env.NODE_ENV! = = 'production' & & warn (`The data property "${key}" is already declared as a prop. `+ `Use prop default value Einstein.`, vm)} else if (! isReserved (key)) {proxy (vm, `_ data`, key) / / Agent defines Getter and Setter}} / / observe data observe (data, true / * asRootData * /)} / / proxy proxy const sharedPropertyDefinition = {enumerable: true, configurable: true, get: noop, set: noop} export function proxy (target: Object, sourceKey: string Key: string) {/ / defines Getter and Setter sharedPropertyDefinition.get = function proxyGetter () {return this [sourceKey] [key] / / when accessing vm.key, you are actually accessing vm [sourceKey] [key] / / the question starting with the above When accessing this.msg is actually visiting this._data.msg} sharedPropertyDefinition.set = function proxySetter (val) {this [sourceKey] [key] = val} / / A pair of vm key has done Getter and Setter Object.defineProperty (target, key, sharedPropertyDefinition)}
To sum up: initialize data in the. / state.js file. Executes the initState () method, which determines that if data is defined, initialize the data.
If data is a function, the getData () method return data.call (vm, vm) is executed. Then compare the attributes defined in data on vm, props on vm, and methods on vm in a loop. If the attribute is defined on data, it cannot be defined in props and methods. Because whether it is defined in data, defined in props, or defined in medthods, it is eventually mounted on the vm instance. See proxy (vm, _ data, key).
Then the Getter and Setter methods are bound to the properties on the vm through the proxy method. Going back to the above question, when accessing this.msg is actually accessing vm._data.msg. So you do have access to the data defined in data in the hook function.
I have to say again, the initialization logic of Vue is written very clearly, the different functional logic is divided into some separate functions to execute, so that the main line logic is clear at a glance, this programming idea is worth learning and learning.
You can add other initialization content yourself, and then look at mounting vm. At the end of initialization, it is detected that if there is an el attribute, the vm.$mount method is called to mount the vm. The goal of mounting is to render the template into the final DOM, so let's explore the mounting process of Vue.
Implementation of Vue instance mounting
In Vue, we mount vm through the $mount instance method. The next step is to explore what the source code does when executing $mount ('# app').
New Vue ({render: h = > h (App),}). $mount ('# app')
The $mount method is defined in multiple files, such as src/platform/web/entry-runtime-with-compiler.js, src/platform/web/runtime/index.js, and src/platform/weex/runtime/index.js. Because the implementation of the $mount method has something to do with the platform and the way it is built.
Select the compiler version of $mount for analysis. The file address is in src/platform/web/entry-runtime-with-compiler.js and the code is as follows:
/ / get the $mount method on the vue prototype, which is stored on the variable mount. Const mount = Vue.prototype.$mount Vue.prototype.$mount = function (el?: string | Element, hydrating?: boolean): Component {/ / query is defined in the'. / util/index' file / / calls the native DOM api querySelector () method. Finally, the el is converted into a DOM object. El = el & & query (el). Return mount.call (this, el, hydrating)}
Reading the code shows that the code first takes the $mount method on the vue prototype, stores it in the variable mount, and then redefines the method. This method handles the incoming el, which can be either a string or a DOM object. The query () method is then called, which is in the. / util/index file. The main thing is to call the native DOM api querySelector () method. Finally, the el is converted to a DOM object and returned. Only the main part of the code is posted above.
The source code also judges the el to determine whether the incoming el is body or html, and if so, it will report a warning in the development environment. Vue cannot be mounted directly to body and html, because it will be overwritten, and the entire document will report an error when html or body is overwritten.
The source code also gets $options to determine whether the render method is defined. If no render method is defined, the el or template string will eventually be compiled into the render () function.
Finally, return mount.call (this, el, hydrating). Mount here is the $mount method on the vue prototype. In the file. / runtime/index. The code is as follows:
Vue.prototype.$mount = function (el?: string | Element, hydrating?: boolean): Component {el = el & & inBrowser? Query (el): undefined return mountComponent (this, el, hydrating)}
Where the parameter el represents the mounted element, which can be a string or a DOM object. If it is a string, the query () method is called in the browser environment to convert it to a DOM object. The second parameter is related to server rendering. We do not need to pass the second parameter in the browser environment. Finally, the mountComponent () method is called when return. The method is defined in src/core/instance/lifecycle.js and the code is as follows:
Export function mountComponent (vm: Component, el:? Element, hydrating?: boolean): Component {vm.$el = el... Let updateComponent / * istanbul ignore if * / if (process.env.NODE_ENV! = = 'production' & & config.performance & & mark) {updateComponent = () = > {const name = vm._name const id = vm._uid const startTag = `vue-perf-start:$ {id} `const endTag = `vue-perf-end:$ {id} `mark (startTag) Const vnode = vm._render () mark (endTag) measure (`vue ${name} render` StartTag, endTag) mark (startTag) vm._update (vnode, hydrating) mark (endTag) measure (`vue ${name} patch`, startTag, endTag)}} else {updateComponent = () = > {vm._update (vm._render (), hydrating)}} new Watcher (vm, updateComponent, noop {before () {if (vm._isMounted & &! vm._isDestroyed) {callHook (vm, 'beforeUpdate')}}, true / * isRenderWatcher * /) hydrating = false / / manually mounted instance, call mounted on self / / mounted is called for render-created child components in its inserted hook if (vm.$vnode = = null) {vm._isMounted = true callHook (vm,' mounted')} return vm}
By reading the code, you can see that this method first instantiates a rendering Watcher, calls the updateComponent method in its callback function, calls the vm._render () method in this method, becomes a virtual DOM node, and finally calls vm._update to update the DOM.
Finally, when the root node is determined, vm._isMounted is set to true, indicating that the instance has been mounted and the mounted hook function is executed. Vm.$vnode represents the parent virtual node of the Vue instance, so if it is Null, it means that it is currently an instance of the root Vue.
So how does vm._render () generate virtual DOM nodes?
_ render () render virtual DOM node
In Vue 2.0, render () is ultimately required for rendering of all Vue components. Vue's _ render () is a private method of the instance, which is used to render the instance as a virtual DOM node. It is defined in the src/core/instance/render.js file and the code is as follows:
Vue.prototype._render = function (): VNode {const vm: Component = this const {render, _ parentVnode} = vm.$options... Let vnode try {currentRenderingInstance = vm vnode = render.call (vm._renderProxy, vm.$createElement)}}
The above code gets the render function from the $options of the vue instance. The _ renderProxy and createElement () methods are called through call () to explore the createElement () method.
CreateElement ()
CreateElement () is in initRender (). As follows:
/ / this function executes initRender () / see the initRender (vm) in the'. / init.js' file in the process of _ init () and passes it into vm. Execute the following method. Export function initRender (vm: Component) {/ / the compiled render function vm._c = (a, b, c, d) = > createElement (vm, a, b, c, d, false) / / the handwritten render function creates the vnode method. Vm.$createElement = (a, b, c, d) = > createElement (vm, a, b, c, d, true)}
InitRender () is the execution of initRender () in the _ init process. See initRender (vm) in the. / init.js file and pass in vm.
In the actual development of vue project, the example of handwritten render function is as follows:
New Vue ({render (createElement) {return createElement ('div', {style: {color:'red'}}, this.msg)}, data () {return {msg: "hello world"}). $mount (' # app')
The performance is better because the handwritten render function eliminates the process of compiling template to render functions.
Next, take a look at the _ renderProxy method:
_ renderProxy
The _ renderProxy method is also executed during the init process. See the file. / init.js, the code is as follows:
Import {initProxy} from'. / proxy' if (process.env.NODE_ENV! = = 'production') {initProxy (vm)} else {vm._renderProxy = vm}
If the current environment is a production environment, assign vm directly to vm._renderProxy
If the current environment is a development environment, initProxy () is executed.
The function is in the. / proxy.js file with the following code:
InitProxy = function initProxy (vm) {/ / determine whether the browser supports proxy. If (hasProxy) {/ / determine which proxy handler to use const options = vm.$options const handlers = options.render & & options.render._withStripped? GetHandler: hasHandler vm._renderProxy = new Proxy (vm, handlers)} else {vm._renderProxy = vm}}
First determine whether the browser supports proxy. It is added by ES6 and is used to set up a layer of "interception" before giving the target object, through which all external access to the object must be intercepted, so it provides a mechanism to filter and rewrite external access.
If the browser does not support proxy, assign vm directly to vm._renderProxy
If the browser supports proxy, new Proxy () is executed.
To sum up: vm._render is done by executing the createElement method and returning the virtual DOM node. So what is a virtual DOM?
Virtual DOM
Before exploring vue's virtual DOM, recommend a virtual DOM open source library. If you have time, interested friends can learn more about it. It uses a function to represent the view layer of an application. View.js uses it for reference to realize virtual DOM. Thus greatly improve the performance of the program. Let's take a look at how vue.js does it.
The definition of vnode is in the src/core/vdom/vnode.js file, as follows:
Export default class VNode {tag: string | void; data: VNodeData | void; children:? Array; text: string | void; elm: Node | void;.}
Virtual DOM is a js object, which is an abstract description of real DOM, such as tag signature, data, child node name, and so on. Because the virtual DOM is only used to map the rendering of a real DOM, it does not include methods to manipulate DOM and methods to manipulate DOM. So it's lighter and simpler. Because the creation of virtual DOM is through the createElement method, how is this link realized?
CreateElement
Vue.js uses the createElement method to create a DOM node, which is defined in the src/core/vdom/create-elemenet.js file as follows:
Export function createElement (context: Component, / / vm instance tag: any,// tag data: any,// data children: any,// child node can construct DOM tree normalizationType: any AlwaysNormalize: boolean): VNode | Array {/ / A pair of inconsistent parameters processing if (Array.isArray (data) | | isPrimitive (data)) {normalizationType = children children = data data = undefined} if (isTrue (alwaysNormalize)) {normalizationType = ALWAYS_NORMALIZE} / / handle the parameters Then _ createElement () is called to actually create the node. Return _ createElement (context, tag, data, children, normalizationType)}
The createElement method encapsulates the _ createElement method, which allows the parameters passed in to be more flexible. After processing these parameters, call the function _ createElement that actually creates the DOM node, as follows:
Export function _ createElement (context: Component, tag?: string | Class | Function | Object, data?: VNodeData, children?: any, normalizationType?: number): VNode | Array {. If (normalizationType = ALWAYS_NORMALIZE) {children = normalizeChildren (children)} else if (normalizationType = SIMPLE_NORMALIZE) {children = simpleNormalizeChildren (children)}...}
The _ createElement method provides five parameters as follows:
Context represents the context of the DOM node, which is of type Component
Tag represents a tag, which can be either a string or a Component
Data represents the data on the DOM node, it is a VNodeData type, and its definition can be found in flow/vnode.js
Children represents the child node of the current DOM node, which is of any type, and then needs to be standardized as a standard VNode array
NormalizationType represents the type of child node specification, the method of different type specification is different, it mainly refers to whether the render function is compiled or handwritten render function.
The flow of the createElement function is a little too much, and this article will focus on the normalization of children and the creation of VNode.
Standardization of children
Virtual DOM (Virtual DOM) is actually a tree structure, and each DOM node may have several child nodes, which should also be the type of VNode.
The fourth parameter children received by _ createElement is of any type, so we need to standardize them as VNode.
Depending on the normalizationType, it calls the normalizeChildren (children) and simpleNormalizeChildren (children) methods, both of which are defined in the src/core/vdom/helpers/normalzie-children.js file as follows:
/ / the render function is called / / when compiled and generated. The array is an one-dimensional array export function simpleNormalizeChildren (children: any) {for (let I = 0; I).
< children.length; i++) { if (Array.isArray(children[i])) { return Array.prototype.concat.apply([], children) } } return children } // 返回一维数组 export function normalizeChildren (children: any): ?Array { return isPrimitive(children) ? [createTextVNode(children)] : Array.isArray(children) ? normalizeArrayChildren(children) : undefined } simpleNormalizeChildren 方法调用场景是 render 函数是编译生成的。但是当子节点为一个组件的时候,函数式组件返回的是一个数组而不是一个根节点,所以会通过 Array.prototype.concat 方法把整个 children 数组拍平,让它的深度只有一层。 normalizeChildren 方法的调用场景有 2 种,一个场景是手写 render 函数,当 children 只有一个节点的时候,Vue.js 从接口层面允许用户把 children 写成基础类型用来创建单个简单的文本节点,这种情况会调用 createTextVNode 创建一个文本节点的DOM 节点;另一个场景是当编译 slot、v-for 的时候会产生嵌套数组的情况,会调用 normalizeArrayChildren 方法,代码如下: function normalizeArrayChildren (children: any, nestedIndex?: string): Array { const res = [] let i, c, lastIndex, last for (i = 0; i < children.length; i++) { c = children[i] if (isUndef(c) || typeof c === 'boolean') continue lastIndex = res.length - 1 last = res[lastIndex] // nested if (Array.isArray(c)) { if (c.length >0) {c = normalizeArrayChildren (c, `${nestedIndex | |'} _ {I}`) / merge adjacent text nodes if (isTextNode (c [0]) & & isTextNode (last)) {res [lastIndex] = createTextVNode (last.text + (c [0]: any) .text) c.shift ()} res.push.apply (res) C)} else if (isPrimitive (c)) {if (isTextNode (last)) {res [lastIndex] = createTextVNode (last.text + c)} else if (c! = =') {res.push (createTextVNode (c))}} else {/ / if both nodes are text nodes Merge them. If (isTextNode (c) & & isTextNode (last)) {res [lastIndex] = createTextVNode (last.text + c.text)} else {if (isTrue (children._isVList) & & isDef (c.tag) & & isUndef (c.key) & & isDef (nestedIndex)) {c.key = `_ _ vlist$ {nestedIndex} _ ${I} _ _ `} res.push (c)} return res}
NormalizeArrayChildren receives 2 parameters.
Children represents the child node to be standardized
NestedIndex represents a nested index; because a single child may be an array type. NormalizeArrayChildren mainly traverses children to get a single node c, and then determines the type of c. If it is an array type, then recursively call normalizeArrayChildren; if it is a basic type, then convert it to VNode type through the createTextVNode method; otherwise, it is already a VNode type. If children is a list and the list is nested, update its key according to nestedIndex.
During the traversal, all three cases are treated as follows: if there are two contiguous text nodes, they are merged into one text node.
At this point, children becomes an Array of type VNode. This is the normalization of children.
Creation of virtual DOM node
Go back to the createElement function. After normalizing children, you need to create an instance of DOM as follows:
Let vnode, ns if (typeof tag = = 'string') {let Ctor ns = (context.$vnode & & context.$vnode.ns) | | config.getTagNamespace (tag) if (config.isReservedTag (tag)) {/ / platform built-in elements vnode = new VNode (config.parsePlatformTagName (tag), data, children, undefined, undefined, context)} else if (Ctor = resolveAsset (context.$options,' components') Tag)) {/ / component vnode = createComponent (Ctor, data, context, children, tag)} else {/ / processing of unknown nodes vnode = new VNode (tag, data, children, undefined, undefined, context)} else {/ / direct component options / constructor vnode = createComponent (tag, data, context, children)}
Here, we first judge the tag. If it is a string type, then if it is some built-in nodes, create a normal VNode directly. If it is a registered component name, then create a component type VNode through createComponent, otherwise create an unknown label VNode. If tag is a Component type, call createComponent directly to create a VNode node of component type.
At this point, the createElement method creates an instance of a virtual DOM tree, which is used to describe a real DOM tree, so how to render it as a real DOM tree? It was actually done by vm._update.
Update renders a virtual DOM as a real DOM
How the _ update method renders a virtual DOM to a real DOM. This part of the code is in the src/core/instance/lifecycle.js file, as follows:
How the _ update method renders a virtual DOM to a real DOM. This part of the code is in the src/core/instance/lifecycle.js file The code is as follows: Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {const vm: Component = this const prevEl = vm._vnode const restoreActiveInstance = setActiveInstance (vm) vm._vnode = vnode if (! prevVnode) {/ / the first rendering of the data executes vm.$el = vm.__patch__ (vm.$el, vnode, hydrating) False / * removeOnly * /)}.}
Reading the code shows that when the data is rendered for the first time, the method vm.__patch__ () is called, and he receives four parameters, combined with the actual development process of our vue project. Vm.$el is a DOM object whose id is app, that is:
; vnode corresponds to the return value of calling the render function; hydrating is false,removeOnly and false in non-server rendering.
The definition of vm.__patch__ method is different in different platforms. The definition of web platform is in src/platforms/web/runtime/index.js. The code is as follows:
/ / is it in the browser environment Vue.prototype.__patch__ = inBrowser? Patch: noop
On the web platform, whether it is server-side rendering also has an impact on this approach. Because there is no real browser DOM environment in server rendering, there is no need to convert VNode into DOM, so it is an empty function. In browser rendering, it points to the patch method, which is defined in the src/platforms/web/runtime/patch.js file, and the code is as follows:
Import * as nodeOps from 'web/runtime/node-ops' import {createPatchFunction} from' core/vdom/patch' import baseModules from 'core/vdom/modules/index' import platformModules from' web/runtime/modules/index' const modules = platformModules.concat (baseModules) export const patch: Function = createPatchFunction ({nodeOps, modules})
Reading the code shows that the return value of the createPatchFunction method is passed into an object, where
NodeOps encapsulates a series of methods for DOM operations
Modules defines the implementation of the hook function of the module; the createPatchFunction method is defined in the src/core/vdom/patch.js file as follows:
Const hooks = ['create',' activate', 'update',' remove', 'destroy'] export function createPatchFunction (backend) {let I, j const cbs = {} const {modules, nodeOps} = backend for (I = 0; I < hooks.length; + + I) {CBS [hooks [I]] = [] for (j = 0; j < modules.length) + + j) {if (isDef (modules [j] [hooks [I])) {cbs [hooks [I]] .push (modules [j] [hooks [I])} /. / / defines some auxiliary functions / / when calling vm.__dispatch__ In fact, it is to call the following patch method / / this applies the return function patch (oldVnode, vnode, hydrating, removeOnly) {/ /. Return vnode.elm}}
CreatePatchFunction defines a series of helper methods and finally returns a patch method, which is assigned to the vm.__patch__ called in the vm._update function. That is to say, when you call vm.__dispatch__, you are actually calling the patch (oldVnode, vnode, hydrating, removeOnly) method, which actually applies the technique of function Curialization.
The patch method receives four parameters, as follows:
OldVnode represents the old VNode node, which may not exist or may be a DOM object
Vnode represents the node of the VNode returned after executing _ render
Hydrating indicates whether it is rendered on the server side
RemoveOnly is for transition-group.
Analyze the patch method, because the incoming oldVnode is actually a DOM container, so the isRealElement is true, then call the emptyNodeAt method to convert the oldVnode into a virtual DOM node (a js object), and then call the createElm method. The code is as follows:
If (isRealElement) {oldVnode = emptyNodeAt (oldVnode)} function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested, ownerArray, index) {if (isDef (vnode.elm) & & isDef (ownerArray)) {vnode = ownerArray [index] = cloneVNode (vnode)} vnode.isRootInsert =! nested / / for transition enter check const data = vnode.data const children = vnode.children const tag = vnode.tag / next determine whether vnode contains tag / / if included, verify the validity of tag in a non-production environment to see if it is a legitimate label / / then call the operation of the platform DOM to create a placeholder element. If (isDef (tag)) {if (process.env.NODE_ENV! = = 'production') {if (data & & data.pre) {creatingElmInVPre++} if (isUnknownElement (vnode, creatingElmInVPre)) {warn (' Unknown custom element:-did you'+ 'register the component correctly? For recursive components,'+ 'make sure to provide the "name" option.', vnode.context)}} / / call the createChildren method to create a child element: vnode.elm = vnode.ns? NodeOps.createElementNS (vnode.ns, tag): nodeOps.createElement (tag, vnode) setScope (vnode) / * istanbul ignore if * / if (_ _ WEEX__) {/ /...} else {/ / call the createChildren method to create child elements / / traverse the child virtual nodes with the createChildren method The recursive call createElm / / passes in vnode.elm as the DOM node placeholder for the parent container during traversal. CreateChildren (vnode, children, insertedVnodeQueue) if (isDef (data)) {invokeCreateHooks (vnode, insertedVnodeQueue)} insert (parentElm, vnode.elm, refElm)} if (process.env.NODE_ENV! = 'production' & & data & & data.pre) {creatingElmInVPre--}} else if (isTrue (vnode.isComment)) {vnode.elm = nodeOps.createComment (vnode.text) insert (parentElm, vnode.elm) RefElm)} else {vnode.elm = nodeOps.createTextNode (vnode.text) insert (parentElm, vnode.elm, refElm)}}
The createElm method creates a real DOM from a virtual node and inserts it into its parent node. Determine whether vnode contains tag. If so, verify the validity of tag in a non-production environment to see if it is a legitimate tag, and then call the operation of platform DOM to create a placeholder element. Then call the createChildren method to create the child element, and the createChildren method code is as follows:
CreateChildren (vnode, children, insertedVnodeQueue) function createChildren (vnode, children, insertedVnodeQueue) {if (Array.isArray (children)) {if (process.env.NODE_ENV! = = 'production') {checkDuplicateKeys (children)} for (let I = 0; I < children.length) + + I) {createElm (children [I], insertedVnodeQueue, vnode.elm, null, true, children, I)} else if (isPrimitive (vnode.text)) {nodeOps.appendChild (vnode.elm, nodeOps.createTextNode (String (vnode.text)}}
The createChildren method traverses the child virtual node, calls createElm recursively, and passes in vnode.elm as the DOM node placeholder of the parent container during traversal. Then call the invokeCreateHooks method to execute all the create hooks and put vnode push into the insertedVnodeQueue. Finally, the insert method is called to insert the DOM into the parent node. Because it is a recursive call, the child element will call insert first, so the insertion order of the whole vnode tree node is the child before the parent. The insert method is defined in the src/core/vdom/patch.js file with the following code:
Insert (parentElm, vnode.elm, refElm) function insert (parent, elm, ref) {if (isDef (parent)) {if (isDef (ref)) {if (Ref [XSS _ clean] = parent) {nodeOps.insertBefore (parent, elm, ref)}} else {nodeOps.appendChild (parent, elm)}
This is the end of the content of "how Vue.js templates and data are rendered into DOM". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.