深入浅出React和Redux
上QQ阅读APP看书,第一时间看更新

1.4.3 Virtual DOM

既然React应用就是通过重复渲染来实现用户交互,你可能会有一个疑虑:这样的重复渲染会不会效率太低了呢?毕竟,在jQuery的实现方式中,我们可以清楚地看到每次只有需要变化的那一个DOM元素被修改了;可是,在React的实现方式中,看起来每次render函数被调用,都要把整个组件重新绘制一次,这样看起来有点浪费。

事实并不是这样,React利用Virtual DOM,让每次渲染都只重新渲染最少的DOM元素。

要了解Virtual DOM,就要先了解DOM, DOM是结构化文本的抽象表达形式,特定于Web环境中,这个结构化文本就是HTML文本,HTML中的每个元素都对应DOM中某个节点,这样,因为HTML元素的逐级包含关系,DOM节点自然就构成了一个树形结构,称为DOM树。

浏览器为了渲染HTML格式的网页,会先将HTML文本解析以构建DOM树,然后根据DOM树渲染出用户看到的界面,当要改变界面内容的时候,就去改变DOM树上的节点。

Web前端开发关于性能优化有一个原则:尽量减少DOM操作。虽然DOM操作也只是一些简单的JavaScript语句,但是DOM操作会引起浏览器对网页进行重新布局,重新绘制,这就是一个比JavaScript语句执行慢很多的过程。

如果使用mustache或者hogan这样的模板工具,那就是生成HTML字符串塞到网页中,浏览器又要做一次解析产生新的DOM节点,然后替换DOM树上对应的子树部分,这个过程肯定效率不高。虽然JSX看起来很像是一个模板,但是最终会被Babel解析为一条条创建React组件或者HTML元素的语句,神奇之处在于,React并不是通过这些语句直接构建DOM树,而是首先构建Virtual DOM。

既然DOM树是对HTML的抽象,那Virtual DOM就是对DOM树的抽象。Virutal DOM不会触及浏览器的部分,只是存在于JavaScript空间的树形结构,每次自上而下渲染React组件时,会对比这一次产生的Virtual DOM和上一次渲染的Virtual DOM,对比就会发现差别,然后修改真正的DOM树时就只需要触及差别中的部分就行。

以ClickCounter为例,一开始点击计数为0,用户点击按钮让点击计数变成1,这一次重新渲染,React通过Virtual DOM的对比发现其实只是id为clickCount的span元素中内容从0变成了1而已:

<span id=”clickCount”>{this.state.count}</span>

React发现这次渲染要做的事情只是更换这个span元素的内容而已,其他DOM元素都不需要触及,于是执行类似下面的语句,就完成了任务:

document.getElementById("clickCount").innerHTML = "1";

React对比Virtual DOM寻找差异的过程比较复杂,在第5章,我们会详细介绍对比的过程。