Übersicht

Bei Erreichen des Mindestbestellwertes entfallen die Versandkosten automatisch Es ist kein Gutscheincode erforderlich
Kein Gutscheincode erforderlich!
Gilt auf Warenkorb # 1. 前言 在上一篇文章中,我们分析了Vue中Diff算法的核心原理,我们知道,Vue在更新过程中,对于新旧两棵虚拟DOM树会在同一层级进行比较,不会跨层级比较。在比较过程中,首先会对新旧两棵虚拟DOM树从根节点开始依次向下循环每一个节点,然后根据不同的节点类型执行不同的操作,其节点类型判断顺序为:**先判断是否是静态节点,然后判断是否是组件节点,然后判断是否是元素节点**。在上一篇文章中我们分析了静态节点和组件节点的情况,那么本篇文章就来分析元素节点的情况。 # 2. 新旧节点都是元素节点 当新旧节点都是元素节点时,那么会走patchVnode方法,在patchVnode方法中,首先会判断新旧节点是否是同一个节点,如果是,则直接返回,啥也不做;如果不是,则继续往下走,判断新旧节点是否是静态节点,如果是,则也直接返回,啥也不做;如果不是,则继续往下走,到这里,就要开始真正的Diff过程了。 真正的Diff过程分为三步: 1. 更新当前节点的属性; 2. 更新子节点; 3. 根据子节点的更新情况,递归更新父节点; 代码如下: javascript function patchVnode ( oldVnode, vnode, insertedVnodeQueue, ownerArray, index, removeOnly ) { // 新旧vnode是完全相同的,直接返回 if (oldVnode === vnode) { return } // 新的vnode是静态节点,直接返回 if (isTrue(vnode.isStatic) && isTrue(oldVnode.isStatic) && vnode.key === oldVnode.key && (isTrue(vnode.isCloned) || isTrue(vnode.isOnce)) ) { vnode.componentInstance = oldVnode.componentInstance return } let i const data = vnode.data if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { i(oldVnode, vnode) } const elm = vnode.elm = oldVnode.elm const oldCh = oldVnode.children const ch = vnode.children // 更新属性 if (isDef(data) && isPatchable(vnode)) { 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)) { if (isDef(oldCh) && isDef(ch)) { if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly) } else if (isDef(ch)) { 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)) { removeVnodes(elm, oldCh, 0, oldCh.length - 1) } else if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, '') } } else if (oldVnode.text !== vnode.text) { nodeOps.setTextContent(elm, vnode.text) } if (isDef(data)) { if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode) } } 接下来,我们就来逐步分析这三步。 ## 2.1 更新当前节点的属性 更新当前节点的属性操作比较简单,就是将新的vnode上的属性更新到真实DOM节点上,同时将旧vnode上已经不存在的属性从真实DOM节点上移除。 具体做法是:首先判断新的vnode上是否有data属性,如果有,则遍历data对象,将里面的属性更新到真实DOM节点上;然后判断旧的vnode上是否有data属性,如果有,则遍历data对象,将里面存在而新的vnode的data对象中不存在的属性从真实DOM节点上移除。 代码如下: javascript // 更新属性 if (isDef(data) && isPatchable(vnode)) { 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) } 其中,cbs中定义了一些生命周期钩子,在patch过程中会调用这些钩子,cbs的定义如下: javascript const hooks = ['create', 'activate', 'update', 'remove', 'destroy'] const cbs = {} 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]]) } } } 可以看到,cbs中的update属性是一个数组,里面存放了一些更新属性的方法,这些方法来自于modules,modules的定义如下: javascript const modules = platformModules.concat(baseModules) 其中,platformModules是与平台相关的模块,baseModules是与平台无关的基础模块。在web平台下,platformModules的定义如下: javascript const platformModules = [ attrs, klass, events, domProps, style, transition ] baseModules的定义如下: javascript const baseModules = [ ref, directives ] 这些模块中定义了各自在create、activate、update、remove、destroy等生命周期钩子中需要做的事情,这里我们就不展开分析了,有兴趣的同学可以自行查看源码。 ## 2.2 更新子节点 更新完当前节点的属性后,接下来就要更新当前节点的子节点了。更新子节点分为以下几种情况: 1. 如果新的vnode没有文本内容(即vnode.text为undefined): - 如果新的vnode和旧的vnode都有子节点,并且子节点不相同,则调用updateChildren方法更新子节点; - 如果新的vnode有子节点,而旧的vnode没有子节点,则先清空旧vnode的文本内容(如果有的话),然后调用addVnodes方法添加新的子节点; - 如果新的vnode没有子节点,而旧的vnode有子节点,则调用removeVnodes方法移除旧的子节点; - 如果新的vnode和旧的vnode都没有子节点,但是旧的vnode有文本内容,则清空文本内容; 2. 如果新的vnode有文本内容,并且与旧的vnode的文本内容不相同,则更新文本内容。 代码如下: javascript // 更新子节点 if (isUndef(vnode.text)) { if (isDef(oldCh) && isDef(ch)) { if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly) } else if (isDef(ch)) { 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)) { removeVnodes(elm, oldCh, 0, oldCh.length - 1) } else if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, '') } } else if (oldVnode.text !== vnode.text) { nodeOps.setTextContent(elm, vnode.text) } 其中,updateChildren方法是Diff算法的核心,我们会在下一篇文章中详细分析。 ## 2.3 递归更新父节点 在更新完当前节点的属性和子节点后,如果当前节点是组件节点,那么还会调用组件的postpatch钩子,这个钩子是在组件更新完成后调用的,可以用来执行一些后续操作。 代码如下: javascript if (isDef(data)) { if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode) } # 3. 总结 本篇文章我们分析了Vue中Diff算法的元素节点更新过程,主要包括三个步骤:更新当前节点的属性、更新子节点、递归更新父节点。其中,更新子节点是Diff算法的核心,我们会在下一篇文章中详细分析。
Bei Erreichen des Mindestbestellwertes entfallen die Versandkosten automatisch Es ist kein Gutscheincode erforderlich
Kein Gutscheincode erforderlich!
Gilt auf personalisierte Artikel Nicht kombinierbar mit anderen Aktionen. Pro Kunde nur einmal einlösbar.
Gilt auf den Warenkorb Du erhältst zusätzlich gestaffelte Rabatte: 5% ab 100 Euro und 12% ab 350 Euro auf deine Bestellung.