vue diff算法全理会(2)

Vue 的 diff 算法是将同层的节点举办较量,所以它的时间巨大度只有 O(n),它的算法很是的高效。 从代码中我们也能看出,patch 中会用 sameVnode 判定老节点和新节点是否是同一个节点,假如是的话才会举办进一步的 patchVnode,不然就会建设新的 DOM,移除旧的 DOM。

sameVnode

下面我们再来看看 sameVnode 中是如何来鉴定两个节点是同一个节点的。

/* 判定两个VNode节点是否是同一个节点,需要满意以下条件 key沟通 tag(当前节点的标签名)沟通 isComment(是否为注释节点)沟通 是否data(当前节点对应的工具,包括了详细的一些数据信息,是一个VNodeData范例,可以参考VNodeData范例中的数据信息)都有界说 当标签是<input>的时候,type必需沟通 */ 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) ) ) ) } // Some browsers do not support dynamically changing type for <input> // so they need to be treated as different nodes /* 判定当标签是<input>的时候,type是否沟通 某些欣赏器不支持动态修改<input>范例,所以他们被视为差异范例 */ function sameInputType (a, b) { if (a.tag !== 'input') return true let i const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) }

sameVnode 通过较量两个节点的 key、tag、注释节点、数据信息是否相等来判定两个 Node 节点是否是沟通节点,对 input 标签做了一个单独的判定,为了兼容差异欣赏器。

patchVnode

// diff算法 较量节点 function patchVnode ( oldVnode, vnode, insertedVnodeQueue, ownerArray, index, removeOnly ) { /*两个VNode节点沟通则直接返回*/ if (oldVnode === vnode) { return } if (isDef(vnode.elm) && isDef(ownerArray)) { // clone reused vnode vnode = ownerArray[index] = cloneVNode(vnode) } const elm = vnode.elm = oldVnode.elm if (isTrue(oldVnode.isAsyncPlaceholder)) { if (isDef(vnode.asyncFactory.resolved)) { hydrate(oldVnode.elm, vnode, insertedVnodeQueue) } else { vnode.isAsyncPlaceholder = true } return } // reuse element for static trees. // note we only do this if the vnode is cloned - // if the new node is not cloned it means the render functions have been // reset by the hot-reload-api and we need to do a proper re-render. /* 假如新旧VNode都是静态的,同时它们的key沟通(代表同一节点), 而且新的VNode是clone可能是标志了once(标志v-once属性,只渲染一次), 那么只需要替换elm以及componentInstance即可。 */ if (isTrue(vnode.isStatic) && isTrue(oldVnode.isStatic) && vnode.key === oldVnode.key && (isTrue(vnode.isCloned) || isTrue(vnode.isOnce)) ) { vnode.componentInstance = oldVnode.componentInstance return } // 执行一些组件钩子 /*假如存在data.hook.prepatch则要先执⾏*/ let i const data = vnode.data if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { /*i = data.hook.prepatch,假如存在的话,见"./create-component componentVNodeHooks"。*/ i(oldVnode, vnode) } // 查找新旧节点是否存在孩子 const oldCh = oldVnode.children const ch = vnode.children // 属性更新 if (isDef(data) && isPatchable(vnode)) { // cbs中关于属性更新的数组拿出来[attrFn, classFn, ...] /*挪用update回调以及update钩子*/ for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode) if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode) } // 判定是否元素 if (isUndef(vnode.text)) { /*假如这个VNode节点没有text文本时*/ // 两边都有孩子 if (isDef(oldCh) && isDef(ch)) { /*新老节点均有children子节点,则对子节点举办diff操纵,挪用updateChildren*/ if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly) } else if (isDef(ch)) { /*假如老节点没有子节点而新节点存在子节点,先清空elm的文本内容,然后为当前节点插手子节点*/ if (process.env.NODE_ENV !== 'production') { checkDuplicateKeys(ch) } if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '') addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue) } else if (isDef(oldCh)) { /*当新节点没有子节点而老节点有子节点的时候,则移除所有ele的子节点*/ removeVnodes(oldCh, 0, oldCh.length - 1) } else if (isDef(oldVnode.text)) { /*当新老节点都无子节点的时候,只是文本的替换,因为这个逻辑中新节点text不存在,所以直接去除ele的文本*/ nodeOps.setTextContent(elm, '') } } else if (oldVnode.text !== vnode.text) { /*当新老节点text纷歧样时,直接替换这段文本*/ nodeOps.setTextContent(elm, vnode.text) } /*挪用postpatch钩子*/ if (isDef(data)) { if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode) } }

patchVnode 的进程是这样的:

假如 oldVnode 和 Vnode 是同一个工具,那么久直接返回,不需要再更新

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

转载注明出处:https://www.heiqu.com/wsjfdz.html