React 虚拟DOM的原理是什么?

26 阅读2分钟

1. 是什么

虚拟 DOM 就是虚拟节点(这句汉化很重要)。React 用 JS 对象来模拟 DOM 节点,
然后将其渲染成真实的 DOM 节点

2. 怎么做

第一步是模拟,用 JSX 语法写出来的 div 其实是一个虚拟节点:

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

这代码会得到这样一个对象:

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

能做到这一点是因为 JSX 语法会被转译为 createElement 函数调用(也叫 h 函数),
如下:

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

第二步是将虚拟节点渲染为真实节点

function render(vdom) {  
    // 如果是字符串或者数字,创建一个文本节点  
    if (typeof vdom === 'string' || typeof vdom === 'number') {  
        return document.createTextNode(vdom)  
    }  
    const { tag, props, children } = vdom  
    // 创建真实DOM  
    const element = document.createElement(tag)  
    // 设置属性  
    setProps(element, props)  
    // 遍历子节点,并获取创建真实DOM,插入到当前节点  
    children  
        .map(render)  
        .forEach(element.appendChild.bind(element))  
    // 虚拟 DOM 中缓存真实 DOM 节点  
    vdom.dom = element  
    // 返回 DOM 节点  
    return element  
}  
function setProps // 略  
function setProp // 略  
// 作者:Shenfq,链接:https://juejin.cn/post/6844903870229905422

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

3. 解决了什么问题

  1. DOM 操作性能问题。通过虚拟 DOM 和 diff 算法减少不必要的 DOM 操作,保证
    性能下限
  2. DOM 操作不方便问题。以前各种 DOM API 要记,现在只有 setState

4. 优点

  1. 为 React 带来了跨平台能力,因为虚拟节点除了渲染为真实节点,还可以渲染为
    其他东西。
  2. 让 DOM 操作的整体性能更好,能(通过 diff)减少不必要的 DOM 操作。

5. 缺点

  1. 性能要求极高的地方,还是得用真实 DOM 操作
  2. React 为虚拟 DOM 创造了合成事件,跟原生 DOM 事件不太一样,工作中要额外
    注意
    a. 所有 React 事件都绑定到根元素,自动实现事件委托
    b. 如果混用合成事件和原生 DOM 事件,有可能会出 bug