React是Facebook于2013年5月推出的一个用来构建用户界面的开源JavaScript框架。React一经推出,便受到国外开发者的追捧,国内的流行始于2014年年中,虽然起步晚,但是发展迅速,目前其相关开发教程和使用文档已遍布国内各大技术社区和论坛,其中不乏一些对比性质的话题,例如:React.js和Angular.js的优劣对比。
React的迅速成名的背后,与其对DOM(文档对象模型,Document Object Model)操作的高性能特性是密不可分的。DOM的渲染效率,一直是前端交互开发的重要性能瓶颈,目前主流JavaScript框架中的操作大量依赖于DOM提供的操作,从而导致了前端交互的效率低下,尤其是在大型复杂的页面渲染中,这种情况表现的比较突出。而React另起炉灶,引入虚拟DOM模式的概念,巧妙绕开了频繁操作DOM的场景,并利用这种模式来处理DOM,从而让其更新操作达到***的高效。
理解虚拟DOM的高性能,需要先了解一下DOM的渲染过程。
DOM渲染过程
所谓DOM渲染,即浏览器将HTML字符串转换成网页视图并渲染视图的过程。首先,浏览器的HTML解析器,会对HTML字符串进行解析,并将它转换成DOM树,同时,CSS解析器也会解析HTML使用到的CSS样式,生成一系列CSS规则;然后浏览器的渲染引擎将DOM树和CSS规则进行整合,并生成一个可用于视图渲染的DOM渲染树;接着确定DOM布局,即每一个节点在浏览器中的确切位置;***一步是进行绘制,将每一个节点的每一个像素绘制在屏幕上,于是我们便看到了期望的网页视图。深入了解这一过程中的复杂,我们可以再对HTML解析器的处理过程做一个特写:HTML解析器中,有两个程序在交替执行: 分词程序和解析程序;分词程序负责将HTML字符串切分成合法的DOM标签字符串,然后交给解析程序处理,解析程序则将其添加到正在构建的DOM树中;当分词程序解析完所有的字符串后,DOM树也便构建完成了。
到这里,我们可以理解DOM的渲染为什么这么慢了: 这个过程,的确是太复杂了。在网页交互中频繁地对DOM进行添加、移除操作,会极大地降低视图渲染的效率,进而降低交互的效率。而在React之前的UI更新操作(包括各大JavaScript框架的UI处理方式),都是先移除原始DOM,再进行新DOM的构建和插入,因而前端交互效率很低,交互体验很差。
React采用了虚拟DOM模式,于是这种情况改变了。
虚拟DOM,简单来说,是一个模拟DOM树的JavaScript对象。React对UI的更新,并没有使用传统的方式: 移除指定区域的所有DOM节点,然后把新节点添加进去,而是采用了差异更新的办法。React会在内存中保留一份指定的原始DOM对应的JavaScript对象,更新之前对比其和新DOM树的JavaScript对象之间的差异,并精确计算出差异所在,然后对存在差异的DOM的属性或文本进行更新。例如,使用Ajax更新列表的内容,由***页转向第二页的过程中,如果两页数据形成的DOM树有完全相同的结构,则不会有任何DOM的删除或添加操作,React仅仅会更新存在差异的原始DOM的属性或文本值。差异更新的办法,将DOM的添加、删除的操作频次降到了***,也因而减轻了DOM渲染效率低的限制,从而让React对UI的操作获得了极大的速度提升。
到了这里,我们可以认识到虚拟DOM的优势所在:差异对比、差异更新。但是对于这种思想,React的实现方式并不一定是***的,比如这种更新方式,需要对虚拟DOM对象进行存储,那必然对内存有额外的消耗。而且,如果浏览器采用了这种方式来更新DOM,这种思想便从源头得到了实现,各大JavaScript框架就无需再关心DOM操作的性能问题,那React的高性能优势是不是就不存在,甚至会变成一种累赘了?