常见基础问题(React篇一)

159 阅读3分钟

虚拟 DOM 的原理是什么?

什么是虚拟DOM?

虚拟 DOM 就是虚拟节点。React 用 JS 对象来模拟 真实DOM 节点,然后将其渲染成真实的 DOM 节点。

如何做到?

第一步

用 JSX 语法写出来的 div 其实就是一个虚拟节点。例如以下代码:

<div id="x">
  <span class="red">hi</span>
</div>

JSX 语法会被转译为 createElement 函数调用(也叫 h 函数)。

React.createElement("div", { id: "x"}, 
  React.createElement("span", { class: "red" }, "hi")
)

会得到类似于这样一个对象:

{
  tag: 'div',
  props: {
    id: 'x'
  },
  children: [
    {
      tag: 'span',
      props: {
        className: 'red'
      },
      children: [
        'hi'
      ]
    }
  ]
}

第二步

将虚拟节点渲染为真实节点。

如果节点发生变化,并不会直接把新虚拟节点渲染到真实节点,而是先经过 diff 算法得到一个 patch 再更新到真实节点上。

解决了什么问题
  1. DOM 操作性能问题。通过虚拟 DOM 和 diff 算法减少不必要的 DOM 操作,保证性能不太差。
  1. DOM 操作不方便问题。以前各种 DOM API 要记,现在只有 setState
优点
  1. 为 React 带来了跨平台能力,因为虚拟节点除了渲染为真实节点,还可以渲染为其他东西。
  1. 让 DOM 操作的整体性能更好,能(通过 diff)减少不必要的 DOM 操作。
缺点
  1. 性能要求极高的地方,还是得用真实 DOM 操作(目前没遇到这种需求)
  1. React 为虚拟 DOM 创造了合成事件,跟原生 DOM 事件不太一样,工作中要额外注意

    1. 所有 React 事件都绑定到根元素,自动实现事件委托
    1. 如果混用合成事件和原生 DOM 事件,有可能会出 bug

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

image.png

什么是DOM diff

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

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

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

      1. 如果没变,就保留对应的真实节点。
      1. 如果变了,就只更新该节点的属性,不重新创建节点。

        1. 更新 style 时,如果多个 css 属性只有一个改变了,那么 React 只更新改变的。
  1. 然后同时遍历两棵树的子节点,每个节点的对比过程同上。

    1. 情况一
    <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 节点插入页面。

    1. 情况二
    <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 文本。

    为什么 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。


React DOM diff 和 Vue DOM diff 的区别?

  1. React 是从左向右遍历对比,Vue 是双端交叉对比(头头对比,尾尾对比,旧头新尾,旧尾新头,再对比Key)。
  1. React 需要维护三个变量,Vue 则需要维护四个变量。
  1. Vue 整体效率比 React 更高,举例说明:假设有 N 个子节点,我们只是把最后子节点移到第一个,那么

    1. React 需要进行借助 Map 进行 key 搜索找到匹配项,然后复用节点
    1. Vue 会发现移动,直接复用节点