Vue内部渲染视图的方法(2)

当oldvnode中不存在,而vnode中存在时,就需要使用vnode新生成真实的DOM节点并插入到视图中。首先如果vnode具有tag属性,则认为它是元素属性,再根据当前环境创建真实的元素节点,元素创建后将它插入到指定的父节点。以上节生成的VNode为例,首次执行

vm._update(vm._render(), hydrating);

vm._render()为上篇生成的VNode,_update函数具体为

Vue.prototype._update = function (vnode, hydrating) { var vm = this; var prevEl = vm.$el; var prevVnode = vm._vnode; var restoreActiveInstance = setActiveInstance(vm); // 缓存vnode vm._vnode = vnode; // Vue.prototype.__patch__ is injected in entry points // based on the rendering backend used. // 第一次渲染,preVnode是不存在的 if (!prevVnode) { // initial render vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */); } else { // updates vm.$el = vm.__patch__(prevVnode, vnode); } restoreActiveInstance(); // update __vue__ reference if (prevEl) { prevEl.__vue__ = null; } if (vm.$el) { vm.$el.__vue__ = vm; } // if parent is an HOC, update its $el as well if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { vm.$parent.$el = vm.$el; } // updated hook is called by the scheduler to ensure that children are // updated in a parent's updated hook. };

因第一次渲染,执行 vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */); ,注意第一个参数是oldVnode为 vm.$el 为元素节点,__patch__函数具体过程为:

(1) 先判断oldVnode是否存在,不存在就创建vnode

if (isUndef(oldVnode)) { // empty mount (likely as component), create new root element isInitialPatch = true; createElm(vnode, insertedVnodeQueue); }

(2) 存在进入else,判断oldVnode是否是元素节点,如果oldVnode是元素节点,则

if (isRealElement) { ... // either not server-rendered, or hydration failed. // create an empty node and replace it oldVnode = emptyNodeAt(oldVnode); }

创建一个oldVnode节点,其形式为

{ asyncFactory: undefined, asyncMeta: undefined, children: [], componentInstance: undefined, componentOptions: undefined, context: undefined, data: {}, elm: div#app, fnContext: undefined, fnOptions: undefined, fnScopeId: undefined, isAsyncPlaceholder: false, isCloned: false, isComment: false, isOnce: false, isRootInsert: true, isStatic: false, key: undefined, ns: undefined, parent: undefined, raw: false, tag: "div", text: undefined, child: undefined }

然后获取oldVnode的元素节点以及其父节点,并创建新的节点

// replacing existing element var oldElm = oldVnode.elm; var parentElm = nodeOps.parentNode(oldElm); // create new node createElm( vnode, insertedVnodeQueue, // extremely rare edge case: do not insert if old element is in a // leaving transition. Only happens when combining transition + // keep-alive + HOCs. (#4590) oldElm._leaveCb ? null : parentElm, nodeOps.nextSibling(oldElm) );

创建新节点的过程

// 标记是否是根节点 vnode.isRootInsert = !nested; // for transition enter check // 这个函数如果vnode有componentInstance属性,会创建子组件,后续具体介绍,否则不做处理 if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { return }

接着在对子节点处理

var data = vnode.data; var children = vnode.children; var tag = vnode.tag; if (isDef(tag)) { ... vnode.elm = vnode.ns ? nodeOps.createElementNS(vnode.ns, tag) : nodeOps.createElement(tag, vnode); setScope(vnode); /* istanbul ignore if */ { createChildren(vnode, children, insertedVnodeQueue); if (isDef(data)) { invokeCreateHooks(vnode, insertedVnodeQueue); } insert(parentElm, vnode.elm, refElm); } if (data && data.pre) { creatingElmInVPre--; } } }

将vnode的属性设置为创建元素节点elem,创建子节点 createChildren(vnode, children, insertedVnodeQueue); 该函数遍历子节点children数组

function createChildren (vnode, children, insertedVnodeQueue) { if (Array.isArray(children)) { for (var i = 0; i < children.length; ++i) { createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i); } } else if (isPrimitive(vnode.text)) { // 如果vnode是文本直接挂载 nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text))); } }

遍历children,递归createElm方法创建子元素节点

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/f5419dafb2500fc990b94ec647c520e9.html