vue2的diff算法

72 阅读4分钟

一、原因

  • 因为操作真实dom的性能消耗会很大,所以vue为了尽量的减少真实dom的操作,特别是新增和删除,
  • 所以它会这样:在「对比子节点」时,vue一切的出发点,都是为了:
  • 1、尽量啥也别做。
  • 2、不行的话,尽量仅改动元素属性。
  • 3、还不行的话,尽量移动元素,而不是删除和创建元素。
  • 4、还不行的话,删除和创建元素。

二、目的

  • 渲染真实DOM的开销是很大,为了实现最小量更新
  • 以某种策略找到新旧两种数据(虚拟DOM)状况的不同来实现最小量更新的办法。

三、发生

  • 当我们当前组件所依赖的数值更新和组件创建时运行update函数。
  • update函数会调用组件的render函数,render生成新的虚拟dom树,
  • update得到新虚拟dom树的根节点,然后进入update函数内部,将旧虚拟dom树替换成新的虚拟dom树,然后用一个变量将旧虚拟dom树保存起来,接下来调用patch函数进行diff比对。

四、算法规则

  • 旧DOM没有子元素,新DOM子元素,清除旧文本 生成新节点
  • 旧DOM子元素, 新没有子元素,清除旧节点 生成新文本
  • 旧DOM没有子元素,新DOM没有子元素,对比文本 进行更新
  • 旧DOM子元素,新DOM子元素,进行diff算法对比

五、是否相同

  • 1、标签类型相同、key相同、如果是input也要比较type是否相同,都相同那就是相同,否则就是不同
  • 2、确定相同,进行属性比较,进行属性更新
  • 3、不同,按顺序继续处理

六、优化策略 - 顺序 - 双端比较法

111.png

  • 当oldStartIdx > oldEndIdx或者newStartIdx > newEndIdx时结束循环。

1、头头比较

  • (1)、相同,把对应的DOM元素移动到新的节点上,新指针旧向右移1个,继续比较,如果一直比较到其中一个虚拟DOM树结束都是相同,比较结束,如果有某个虚拟DOM树还有节点没有比较,如果是新虚拟DOM树直接在节点上新增DOM元素,如果是老的直接删除节点和对应DOM元素。
  • (2)、不相同,走下面2。

2、尾尾比较

  • (1)、和上面的方式一样,是相同的新旧指针都向左移1个,继续比较,不同走下面的3。

3、头尾比较(旧,新)

  • (1)、相同,把旧节点上DOM元素移动到对应的新节点上,旧头指针后移、新尾指针前移,一直到某一个结束,和1结束一样处理
  • (2)、不同,走下面的4

4、尾头比较(旧,新)

  • (1)、和上面的方式一样,相同就移动DOM到新的节点上,旧尾指针前移、新头指针后移,一直到某一个结束,和1结束一样处理
  • (2)、不同,走下面的5

5、以新树头节点为基准查找旧树

  • (1)、拿新数组的第一个节点去老数组中去查找,找不到,在新指针上创建新的DOM,新指定向右移,继续从1开始比较。
  • (2)、找到了,移动旧指针的DOM元素到新指针上,然后新指针向后移,继续从1开始比较,旧指针对他的节点设置undefined

七、key 的作用

  • key 作为节点的唯一标识,在 Diff 过程中能够帮助 Vue 2 准确地复用相同的节点。当节点有 key 时,Vue 可以根据 key 快速找到可复用的节点,避免不必要的 DOM 操作。

八、其他作者总结

1、初始化指针 :为新旧子节点列表分别设置首尾指针。

2、 循环比较 :在oldStartIdx <= oldEndIdx 且 newStartIdx <= newEndIdx 条件下,持续比较:

-   **头头比较** :若旧头与新头节点相同,更新节点,指针后移。
-   **尾尾比较** :若旧尾与新尾节点相同,更新节点,指针前移。
-   **头尾比较** :若旧头与新尾节点相同,更新节点并移动 DOM 位置,旧头指针后移、新尾指针前移。
-   **尾头比较** :若旧尾与新头节点相同,更新节点并移动 DOM 位置,旧尾指针前移、新头指针后移。
-   **其他情况** :在旧列表找与新头节点 `key` 相同的节点,有则移动更新,无则创建新节点插入。

3、循环结束处理

-   若旧列表先遍历完,将新列表剩余节点插入 DOM。
-   若新列表先遍历完,移除旧列表剩余节点对应的 DOM。