react之虚拟DOM

1,091 阅读3分钟

什么是虚拟DOM

在浏览器中,虚拟DOM是指用js对象来表示页面上的元素,并提供了操作DOM对象的API。

在框架中,虚拟DOM是指用js对象来模拟 页面上DOM和DOM嵌套。

在 React 中,render 执行的结果得到的并不是真正的 DOM 节点,结果仅仅是轻量级的 JavaScript 对象,我们称之为 virtual DOM。

虚拟DOM的本质

一个虚拟DOM(元素)是一个一般的js对象,准确的说是一个对象树(倒立的);虚拟DOM保存了真实DOM的层次关系和一些基本属性,与真实DOM一一对应;如果只是更新虚拟DOM,页面是不会重绘的。

虚拟DOM的目的

实现页面上的元素高效更新

虚拟DOM基本原理

用JS对象树表示DOM树的结构;然后用这个树构建一个真正的DOM树插到文档当中;当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;把差异应用到真实DOM树上,视图就更新了。

比如页面上有一个表格,需求现在要更改表格中的其中某一项,需求只需要更改表格中要更改的那一项,其余的不改变,这样性能才是做好的,简单的来说就是按需更新。

虚拟DOM的实现过程

DOM树概念

简单来说就是由文档及文档中的所有的元素(标签)组成的一个树形结构图,叫树状图(DOM树)

react中创建虚拟DOM

  1. 原生js创建(基本不使用)
React.createElement('h1', {id:'myTitle'}, title)
  1. 使用jsx创建(下一篇具体讲解)

简单流程

  1. 通过原生js生成DOM
// 原生js创建(基本不使用)
let virtualDom1 = createElement('ul', {class: 'list'}, [
    createElement('li', {class: 'item'}, ['a']),
    createElement('li', {class: 'item'}, ['b']),
    createElement('li', {class: 'item'}, ['c']),
])

// jsx语法创建
<div className="confirm">
    <Mask onClick={()=> cancel('123')}/>
    <div className='cf_box'>
        <div className='cf_feild'>
            <h6>{title || '提示'}<Icon type="close" onClick={()=> cancel('123')} /></h6>
        </div>
    </div>
</div>


let patchs = diff(virtualDom1, virtualDom2);
  1. 渲染到页面上
 // 原生js
let el = render(virtualDom);
renderDom(el, window.root);
// jsx  直接写在render函数即可
render() {
    const {onclick} = this.props;
    return (
        <div className='mask_box' onClick={()=> onclick && onclick()}>

        </div>
    )
}
  1. 更新时,使用diff算法比较,改变虚拟DOM
  2. 按需更新

React diff算法

  1. tree diff

新旧两颗dom树,逐层对比的过程,就是treediff;当整棵DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到。

注意点

只会对相同颜色方框内的 DOM 节点进行比较,即同一个父节点下的所有子节点。

当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。

  1. component diff

在进行tree Diff 的时候,每一层中,组件级别的对比,叫做Component Diff。

注意点

当 component D 改变为 component G 时,即使这两个 component 结构相似,一旦 React 判断 D 和 G 是不同类型的组件,就不会比较二者的结构,而是直接删除 component D,重新创建 component G 以及其子节点。

  1. element diff

在进行组件对比的时候,如果两个组件类型相同,则需要进行元素级别的对比,这叫做Element Diff。

注意点

新老集合所包含的节点,如下图所示,新老集合进行 diff 差异化对比,通过 key 发现新老集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将老集合中节点的位置进行移动,更新为新集合中节点的位置,此时 React 给出的 diff 结果为:B、D 不做任何操作,A、C 进行移动操作,即可。