React 或 Vue 的 DOM diff 算法是怎样的?

311 阅读2分钟
  1. 是什么

    DOM diff 就是对比两棵虚拟 DOM 树的算法(废话很重要)。当组件变化时,会 render 出一个新的虚拟 DOM,diff 算法对比新旧虚拟 DOM 之后,得到一个 patch,然后 React 用 patch 来更新真实 DOM。

  2. 怎么做

    1. 首先对比两棵树的根节点

      1. 如果根节点的类型改变了,比如 div 变成了 p,那么直接认为整棵树都变了,不再对比子节点。此时直接删除对应的真实 DOM 树,创建新的真实 DOM 树。
      2. 如果根节点的类型没变,就看看属性变了没有
        1. 如果没变,就保留对应的真实节点

        2. 如果变了,就只更新该节点的属性,不重新创建节点。

          a. 更新 style 时,如果多个 css 属性只有一个改变了,那么 React 只更新改变的。

    2. 然后同时遍历两棵树的子节点,每个节点的对比过程同上。

      情况一

      <ul>
        <li>A</li>
        <li>B</li>
      </ul>
      
      <ul>
        <li>A</li>
        <li>B</li>
        <li>C</li>
      </ul>
      

      React 依次对比 A-A、B-B、空-C,发现 C 是新增的,最终会创建真实 C 节点插入页面。 情况二

      <ul>
        <li>B</li>
        <li>C</li>
      </ul>
      
      <ul>
        <li>A</li>
        <li>B</li>
        <li>C</li>
      </ul>
      

      React 对比 B-A,会删除 B 文本新建 A 文本;对比 C-B,会删除 C 文本,新建 B 文本;(注意,并不是边对比边删除新建,而是把操作汇总到 patch 里再进行 DOM 操作。)对比空-C,会新建 C 文本。

      你会发现其实只需要创建 A 文本,保留 B 和 C 即可,为什么 React 做不到呢?

      因为 React 需要你加 key 才能做到:

      <ul>
        <li key="b">B</li>
        <li key="c">C</li>
      </ul>
      
      <ul>
        <li key="a">A</li>
        <li key="b">B</li>
        <li key="c">C</li>
      </ul>
      

      React 先对比 key 发现 key 只新增了一个,于是保留 b 和 c,新建 a。

      注意:Vue 的diff算法才是「双端交叉对比」算法

      双端交叉对比的 具体的diff分析 canyuegongzi.github.io/web/vue/3.h… juejin.cn/post/697162…