基于React理解虚拟DOM(Virtual DOM)和Diff
我在之前的文章《虚拟DOM(Virtual DOM)中动态更新视图的diff算法》中,基于vue描述Virtual DOM(下面可以称为vm
)和Diff算法。由于vue2.X中的vm
是建立在第三方库snabbdom
之上的,所以我们通过分析了snabbdom
的关键源码片段讲述了其中几个重要方法。如createElement、render、patch
等,如果你感兴趣,可以点击文中的链接进行阅读。
在react中,vm
和diff算法
有很大的相似之处,本篇文章主要从逻辑上进行描述,并不深入源码去分析。
首先,我们需要清楚一个概念,那就是render
函数的执行时机。简单一句话就可以概括。
当组件的
state
或者props
发生改变的时候,render函数就会重新执行
一、几种视图渲染的设计方案
下面是一个很简单的React组件的代码。从代码中我们可以看出,React组件有state(数据)
和JSX
模版组成。那么要从数据+模版到最终生成页面的DOM
,怎么样设计才是最高效、性能最有的技术方案呢。
import React, { Component } from 'react'
class TodoItem extends Component{
constructor(props){
super(props)
}
render(){
return (<li onClick={this.handleClick.bind(this)}>
{this.props.content}
</li>)
}
handleClick(){
this.props.deleteItem(this.props.index)
}
}
export default TodoItem
1、直接渲染成真实DOM
这是一种最简单,最直接,也是最容易被想到的一种方法。具体的流程如下:
1、state数据
2、JSX模版
3、数据+模版 结合,生成真实DOM来显示
4、state发生改变
5、数据 + 模版 结合,生成真实DOM,替换原来的DOM
缺陷
1、第一次生成一个完整的DOM片段
2、第二次生成一个完整的DOM片段
3、第二次的DOM替换第一次的DOM,非常耗性能
2、对比文档碎片(DocumentFragment)修改已发生改变的DOM片段
这种方法是通过对比前后两次生成DOM的不同(对比时,第二次生成的文档碎片),来修改仅仅有发生改动的DOM片段。具体的流程如下:
1、state数据
2、JSX模版
3、数据+模版 结合,生成真实DOM来显示
4、state发生改变
5、数据+模版 结合,生成真实DOM,并不直接替换原始的DOM
6、新的DOM(DocumentFragment)和原始的DOM做对比,找差异
7、找到变化(如input的值
)
8、只用新的DOM中的input
元素,替换掉老的DOM中的input
元素
缺陷
虽然减少了DOM更新的体积,但是增加了DOM比较的操作,所以性能提升不明显
3、采用虚拟DOM和Diff算法
这种方法是react
和vue
所采用的渲染方法,主要是通过用JS对象模拟原生DOM,进行一系列的对比、更新操作。由于JS的执行效率很高,再加上Diff算法的巧妙设计,使得这种方法效率也非常高,已成为前端世界中的明星。具体的步骤如下:
1、state数据
2、JSX模版
3、生成虚拟DOM(虚拟DOM就是一个JS对象,用它来描述真实DOM)()
['div',{id: 'abc'},['span', {}, 'hello world']]
4、用虚拟DOM生成真实DOM,来显示。
<div id="abc"><span>hello world</span></div>
5、state发生改变
6、数据+模版 生成新的虚拟DOM()
7、比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容()
8、直接操作DOM,改变span中的内容
我们需要注意下面几点
1、JSX并不是虚拟DOM,只是模版,让我们编码更加简洁。
2、render函数的作用主要是将虚拟DOM渲染成真实DOM,可以直接写html标签
,也可以写虚拟DOM
html标签
render(){
return <div><span>item</span></div>
}
虚拟DOM
render(){
return React.createElement('div', {}, React.createElement('span', {}, 'item'))
}
优点
1、性能提升了。
2、它使得跨端应用得以实现。React Native。vm
在浏览器端生成DOM,在原生端生成对应平台所需要的组件。
二、虚拟DOM中的Diff算法
React中vm
中的diff算法跟vue中diff算法原理相同。我们将从下面三点具体看一下Diff算法。
1、React中异步更新数据
在React中,我们不能直接对state中的数据进行操作,必须通过
this.setState```这个方法进行操作,那么这样做有好处吗?肯定是有的。React 底层将改变多次state的改变合并为一次state的改变,减少了更新操作,提升了性能。
2、前后虚拟DOM对比时采用“同级比较”
这个对比的方式跟vue中一致。从第一层开始对比,如果第一层没有改变(key和$el),则进行第二层的对比,依次往下。如果第一层发生了改变,直接用新的节点替换旧的节点。
前后虚拟DOM对比时采用“同级比较”
3、列表数据渲染时key值要固定
在列表渲染的时候,我们要为每一条数据指定唯一的key值,并且这个key值要固定。如果没有key或者key值不确定的情况下,在diff算法前后对比的过程中,前后节点对照关系将不明确。无法做到只修改已经改变的节点,不改变没有发生变化的节点。所以我们不推荐使用循环时的索引值index
来做key值。
至此,文章结束,感谢您的阅读。