快捷搜索:  汽车  科技

vue 什么时候发布3.0(写于vue3.0发布前夕的helloworld之四)

vue 什么时候发布3.0(写于vue3.0发布前夕的helloworld之四)继续到我们得patch得方法,接着会调emptyNodeAt,创造一个tag是div得空Vnode,然后将挂载上得div元素引用和body得引用拿到,使用createElm方法创建真实得dom节点:function sameVnode (a b) { return ( a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFact

OK。接上回到render:

with(this){return _c('div' {attrs:{"id":"app"}} [_v(_s(msg))])}

接着开始执行_s:一言以蔽之,就是undefined 和null输出一个空字符串,其他类型需要转换,然后开始执行_v方法:

function createTextVNode (val) { return new VNode(undefined undefined undefined String(val)) }

就是创建了一个虚拟的文本节点,然后执行_c方法:

vm._c = function (a b c d) { return createElement(vm a b c d false); };

这个之前有提过,等执行完这些方法之后,我们就会有一个虚拟dom节点,长这样:

vue 什么时候发布3.0(写于vue3.0发布前夕的helloworld之四)(1)

至此,vm._render执行完毕,代码最终返回到updateComponent方法里,开始执行,vm._update:

Vue.prototype._update = function (vnode hydrating) { var vm = this; var prevEl = vm.$el; var prevVnode = vm._vnode; var restoreActiveInstance = setActiveInstance(vm); vm._vnode = vnode; // Vue.prototype.__Patch__ is injected in entry points // based on the rendering backend used. 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. };

和其他函数一样一开始先拿一些所需用到的变量,这里因为是第一次挂载,prevVnode为undefined,__patch__方法执行remove移除策略,代码注释中也有讲到:

function patch (oldVnode vnode hydrating removeOnly) { if (isUndef(vnode)) { if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); } return } var isInitialPatch = false; var insertedVnodeQueue = []; if (isUndef(oldVnode)) { // empty mount (likely as component) create new root element isInitialPatch = true; createElm(vnode insertedVnodeQueue); } else { var isRealElement = isDef(oldVnode.nodeType); if (!isRealElement && sameVnode(oldVnode vnode)) { // patch existing root node patchVnode(oldVnode vnode insertedVnodeQueue null null removeOnly); } else { if (isRealElement) { // mounting to a real element // check if this is server-rendered content and if we can perform // a successful hydration. if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) { oldVnode.removeAttribute(SSR_ATTR); hydrating = true; } if (isTrue(hydrating)) { if (hydrate(oldVnode vnode insertedVnodeQueue)) { invokeInsertHook(vnode insertedVnodeQueue true); return oldVnode } else { warn( 'The client-side rendered virtual DOM tree is not matching ' 'server-rendered content. This is likely caused by incorrect ' 'HTML markup for example nesting block-level elements inside ' '<p> or missing <tbody>. Bailing hydration and performing ' 'full client-side render.' ); } } // either not server-rendered or hydration failed. // create an empty node and replace it oldVnode = emptyNodeAt(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) ); // update parent placeholder node element recursively if (isDef(vnode.parent)) { var ancestor = vnode.parent; var patchable = isPatchable(vnode); while (ancestor) { for (var i = 0; i < cbs.destroy.length; i) { cbs.destroy[i](ancestor); } ancestor.elm = vnode.elm; if (patchable) { for (var i$1 = 0; i$1 < cbs.create.length; i$1) { cbs.create[i$1](emptyNode ancestor); } // #6513 // invoke insert hooks that may have been merged by create hooks. // e.g. for directives that uses the "inserted" hook. var insert = ancestor.data.hook.insert; if (insert.merged) { // start at index 1 to avoid re-invoking component mounted hook for (var i$2 = 1; i$2 < insert.fns.length; i$2 ) { insert.fns[i$2](); } } } else { registerRef(ancestor); } ancestor = ancestor.parent; } } // destroy old node if (isDef(parentElm)) { removeVnodes(parentElm [oldVnode] 0 0); } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode); } } } invokeInsertHook(vnode insertedVnodeQueue isInitialPatch); return vnode.elm } }

patch方法中,首先会初始化isRealElement变量,这里我们我们的oldVnode调用的时候直接传的是div的引用,故而为true,所以if-else分支走了最下面的一个,当然这是第一次挂载,当为更新的时候,isRealElement的值为false,就会执行patchVnode方法,去patch新旧Vnode, 这里注意,patch的时候,patchVnode,只会在两个vnode是相同的时候才会patch,而什么时候是两个相同的vnode呢?就是下面这个方法的返回值为真的时候:

function sameVnode (a b) { return ( a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) ) }

所以看到key值得重要性了么。

继续到我们得patch得方法,接着会调emptyNodeAt,创造一个tag是div得空Vnode,然后将挂载上得div元素引用和body得引用拿到,使用createElm方法创建真实得dom节点:

function createElm ( vnode insertedVnodeQueue parentElm refElm nested ownerArray index ) { if (isDef(vnode.elm) && isDef(ownerArray)) { // This vnode was used in a previous render! // now it's used as a new node overwriting its elm would cause // potential patch errors down the road when it's used as an insertion // reference node. Instead we clone the node on-demand before creating // associated DOM element for it. vnode = ownerArray[index] = cloneVNode(vnode); } vnode.isRootInsert = !nested; // for transition enter check if (createComponent(vnode insertedVnodeQueue parentElm refElm)) { return } var data = vnode.data; var children = vnode.children; var tag = vnode.tag; if (isDef(tag)) { { if (data && data.pre) { creatingElmInVPre ; } if (isUnknownElement$$1(vnode creatingElmInVPre)) { warn( 'Unknown custom element: <' tag '> - did you ' 'register the component correctly? For recursive components ' 'make sure to provide the "name" option.' vnode.context ); } } 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--; } } 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); } }

一进来得if是说如果当前得vnode在前一个render中,现在被当作一个新的vnode使用时,重写他的elm属性可能会引发后续得patch error,这个时候应该clone他。下来会使用createComponent方法做个检查,接着拿到vnode得data,children,tag,以上三个都是创建dom节点时需要用到的,setscope之后创建children节点,createChildren就是循环children依次调用createElm完成得,这里子节点因为是文本节点最后调用了nodeOps.createTextNode完成创建,并插入到父级得el上,然后会使用invokeCreateHooks更新dom属性,最后将完成得新的dom节点插入到body上。

执行完createElm方法后,就会将新生成得dom节点插入到dom树上,然后我们得代码继续走,返回到patch方法中,移除原来得dom节点。然后patch执行完毕,返回到Vue.prototype._update方法中,执行一些标记操作,然后继续返回,直到返回到Watcher.prototype.get中,接下来执行popTarget方法,将当前watcher实例从targetStack中拿出来,并将dep.target设置为undefined,这也预示着此次依赖收集已经结束,接着执行Watcher.prototype.cleanupDeps重新初始化deps属性,然后回到依赖收集最开始得那条语句,也就是watcher得构造函数里的:

this.value = this.lazy ? undefined : this.get();

至此,dom挂载结束,依赖收集完毕,然后回到mountComponent方法,唤起mounted生命周期钩子,层层返回。然后源码版hello wrold至此结束。随后还会有一个异步代码,唤起devtools,这是后话。

猜您喜欢: