美文网首页
从项目中再看vue diff 算法

从项目中再看vue diff 算法

作者: 冷暖自知_zjz | 来源:发表于2023-08-08 11:10 被阅读0次

页面如下

diff1.jpg diff1.jpg diff2.jpg diff2.jpg

代码如下

code1.png code1.png code2.jpg code2.jpg

问题:当点击HelloWorld 的改变isShow的值从false变为true

会打印HelloWorld-mounted 也就是说HelloWorld组件的mouted 执行了,也就是HelloWorld组件销毁重建了。从代码看HelloWorld组件上没有v-if 所以不是业务代码控制的是否销毁,那么还有那种情况呢。对,就是vue-diff算法对比过程中销毁重建了。从代码上看。是指在代码的收尾各加了一个div。为什么会出现HelloWorld组件销毁重建呢?

duandian1.jpg duandian1.jpg

从上图中可以看出newCh 新的节点和oldCh老的节点都是长度为4的虚拟节点数组。diff 的时候会两头两尾,头尾、尾头两两对比。都不满足后会走到updateChildren 的while 里面的else 从老的节点数组中查找是否有和newStartVnode相同的节点sameVnode 所以找到了 老的里面的第二个和新的里面的第一个是相同节点。

duandian2.jpg duandian2.jpg

在oldCh数组里面索引为1的也就是第二个就是 HelloWorld 组件会被销毁

再次while 时 如下图

duandian3.jpg duandian3.jpg

是拿老的里面的索引为2的和新的里面索引为1的被判断为相同的。newCh中的 HelloWorld 组件会被创建 所以HelloWorld组件的mounted 再次执行

vue-diff.png vue-diff.png

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)
      )
    )
  )
}
// 虚拟节点中所包含的属性
export default class VNode {
tag: string | void; // 标签名
data: VNodeData | void; // 数据
children: ?Array; // 子节点数组
text: string | void;  // 真实节点所对应的文本
elm: Node | void;  // 对应的真实dom
key: string | number | void; // 元素上定义的key
parent: VNode | void; // component placeholder node
isStatic: boolean; // hoisted static node  // 是否是静态节点
isComment: boolean; // 是否是注释节点
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?  // 是否是v-once的节点
}
// 更新子节点
function updateChildren (parentElm, oldCh, newCh) {
    // 定义两个指针分别指向两个子节点数组的开始
    let oldStartIdx = 0
    let newStartIdx = 0
    // 分别获取新老子节点数组的长度和四个开始和结束节点
    let oldEndIdx = oldCh.length - 1
    let oldStartVnode = oldCh[0]
    let oldEndVnode = oldCh[oldEndIdx]
    let newEndIdx = newCh.length - 1
    let newStartVnode = newCh[0]
    let newEndVnode = newCh[newEndIdx]
    let oldKeyToIdx, idxInOld, vnodeToMove, refElm
    // 当新老节点都没有对比完一直循环
    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
      // 老节点的开始节点不存在,则直接进行下次循环
      if (isUndef(oldStartVnode)) {
        oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
        
      }
      // 老节点的结束节点不存在,则直接进行下次循环
      else if (isUndef(oldEndVnode)) {
        oldEndVnode = oldCh[--oldEndIdx]
      }
      // 判断新老子节点的开始节点是否值比较
      else if (sameVnode(oldStartVnode, newStartVnode)) {
        // 递归比较两个节点
        patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
        // 移动两个指针
        oldStartVnode = oldCh[++oldStartIdx]
        newStartVnode = newCh[++newStartIdx]
      }
      // 同上
      else if (sameVnode(oldEndVnode, newEndVnode)) {
        patchVnode(oldEndVnode, newEndVnode)
        oldEndVnode = oldCh[--oldEndIdx]
        newEndVnode = newCh[--newEndIdx]
      }
      // 首尾比较
      else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
        patchVnode(oldStartVnode, newEndVnode)
        canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
        oldStartVnode = oldCh[++oldStartIdx]
        newEndVnode = newCh[--newEndIdx]
      }
      // 首尾比较
      else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
        patchVnode(oldEndVnode, newStartVnode)
        canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
        oldEndVnode = oldCh[--oldEndIdx]
        newStartVnode = newCh[++newStartIdx]
      }
      // 使用v-for 建议添加key 当两首两尾首尾都不值得比较,这个时候如果定义了key就会通过key查找是否值得比较
      else {
        if (isUndef(oldKeyToIdx)) 
            // createKeyToOldIdx会返回一个map key就是老元素的key值是老元素的索引(oldKeyToIdx)
            oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
        // findIdxInOld方法 (在老的里面查找出值得比较的元素)
        idxInOld = isDef(newStartVnode.key)
          ? oldKeyToIdx[newStartVnode.key]
          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
        // idxInOld (老子节点数组中值得比较的节点的索引位置)
        if (isUndef(idxInOld)) { // New element
         // 说明新子节点在老子节点数组中不存在,直接创建新的元素
          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
        } else {
          vnodeToMove = oldCh[idxInOld]
          // 值得比较
          if (sameVnode(vnodeToMove, newStartVnode)) {
            patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)
            oldCh[idxInOld] = undefined
            canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
          } else {
            // key 相同但是元素不同,按新元素处理
            createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
          }
        }
        // 移动指针
        newStartVnode = newCh[++newStartIdx]
      }
    }
    // 新的子节点数组长度长 添加新增的
    if (oldStartIdx > oldEndIdx) {
      refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
      addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx)
    } else if (newStartIdx > newEndIdx) {
      //老的子节点数组长度长 删除多余的
      removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
    }
  }

相关文章

  • sammary

    vue-diff算法 react 性能优化 diff算法 ,局部更新DOMshouldComponentUpdat...

  • Vue2.0的Diff算法

    原文:解析Vue2.0的Diff算法 Vue2.0加入了Virtual Dom,Vue的Diff位于patch.j...

  • VueDiff算法的简单分析和一些个人思考

    Diff算法是Vue视图动态改变的核心算法之一 本文包括对Diff算法的简单概括,和我闲的难受对Diff算法的一些...

  • vue系列---vue-diff

    1.vue-diff 是什么? 提到vue的diff算法就不得不提一个名词 虚拟dom(Virtual DOM) ...

  • 理解vue2.x之diff算法

    了解diff算法前,应该先了解虚拟DOM(VNode),在vue中是先创建VNode,再通过diff算法看哪个节点...

  • vue3是如何变快的

    看了李南江老师的课程,将内容做一个记录 diff算法优化 vue2diff算法是对节点进行全量对比vue3.0在创...

  • Vue.js 3.0 对比 Vue 2

    1、Vue 3.0 性能提升主要是通过哪几方面体现的? 一.编译阶段 diff 算法优化vue3 在 diff 算...

  • vue和react的diff算法比较

    相同点:Vue和react的diff算法,都是不进行跨层级比较,只做同级比较。 不同点: 1.Vue进行diff时...

  • 第十七天

    1.你怎么理解vue中的diff算法? diff算法是虚拟DOM技术的必然产物:通过新旧虚拟DOM作对比(即dif...

  • 0725-

    1.virtual DOM 和 Diff算法? 2.vue生命周期 3.vue-router原理 4.vue通信 ...

网友评论

      本文标题:从项目中再看vue diff 算法

      本文链接:https://www.haomeiwen.com/subject/jbehpdtx.html