这件事还要从 2013 年那个秋天提及。javascript
这实际上很是快,主要是由于大多数DOM操做每每很慢。DOM上有不少性能工做,但大多数DOM操做都会丢帧。html
对!就是这张图,这张图把你们引入了 DOM
操做是昂贵且慢的,Virtual DOM
是快速的思惟里。前端
6 年后的今天,React
已经风靡全球,Virtual DOM
也受到了你们的承认,国产之星 VUE
也使用了 Virtual DOM
。java
那么问题来了,Virtual DOM
真的快吗?Virtual DOM
的意义究竟是什么?咱们为何要使用 Virtual DOM
?react
咱们都据说直接更新文档对象模型(DOM
)效率低且速度慢。可是,咱们中不多有人真的有数据支持它。关于React虚拟DOM的讨论是,它是一种更有效的方式来更新 Web
应用程序中的视图,但咱们不多有人知道为何以及这种效率是否会致使更快的页面渲染时间。git
抛开使用 React
的其余好处,例如单向数据绑定和组件,我将讨论 Virtual DOM
究竟是什么,以及它是否可以证实 React
比其余UI库更合理(或者根本没有UI库) 。github
咱们为何须要 UI 库呢?算法
我敢确定,如今的前端界,很大一部分人离开了三大框架以后就不知道该怎么办了,他们可能理所固然的认为视图和数据是绑定的(VUE),或者直接使用 setState
来更新视图(React)。npm
有了 UI
库以后,咱们能够直接数据与视图绑定,而不须要再操做 DOM
。浏览器
这里不会详细的讲 DOM
,只会粗略带过。
DOM
表明文档对象模型,是结构化文本的抽象。对于Web
开发人员,此文本是HTML
代码,DOM
简称为*HTML DOM
。HTML
的元素成为DOM
中的节点*。
HTML DOM
提供了一个用于遍历和修改节点的接口(API)。它包含像getElementById
或的方法removeChild
。咱们一般使用JavaScript
语言来处理DOM
,由于......好吧,没人知道为何:)。
所以,每当咱们想要动态地更改网页的内容时,咱们都会修改DOM
:
var item = document.getElementById("myLI");
item.parentNode.removeChild(item);
复制代码
document
是根节点的抽象getElementById
,parentNode
并且removeChild
是来自HTML DOM API
的方法。
那么问题来了,因为HTML DOM
始终是树形结构,咱们能够很容易地遍历每一个节点,可是现在Web APP
的当下,DOM
树愈来愈大,咱们须要不停的修改大量的DOM
树。这是真正使人痛苦的地方。
咱们一般是如下一个流程来更新 DOM
:
这明显有几个问题:
bug
。更新DOM
并不慢,就像更新任何JavaScript
对象同样。
那到底是什么让更新真正的DOM
变慢?
是绘制。
布局过程当中,绘制占用了大部分时间。
结合下图,以及此文章,你会明白,更新 DOM
的真正问题是屏幕的绘制。
负责在浏览器屏幕上显示或呈现网页的渲染引擎解析HTML
页面以建立DOM
。它还解析CSS
并将CSS
应用于HTML
,从而建立渲染树,此过程称为**attachment
**。
因此,当咱们这样作时
document.getElementById('elementId').innerHTML="New Value"
复制代码
发生如下事情:
HTML
elementId
的子元素DOM
CSS
从新计算CSS和更改布局使用复杂的算法,它们会影响性能。
所以,更新真正的DOM
并不只仅涉及更新DOM
,而是涉及许多其余过程。
此外,上述每一个步骤都针对真实DOM
的每次更新运行,即若是咱们更新真实DOM
10次,则上述步骤中的每个将重复10次。这就是为何更新 DOM
很慢的缘由。
首先 , Virtual DOM
不是由 React
发明的,但React
使用它并免费提供。
因为 DOM
操做的复杂性,Virtual DOM
被创造了出来,他以一个虚拟树的状态,存储在内存中,再映射到真实的 DOM
,每次更新,都是虚拟树的对比,再将差别部分进行更新,并反映到真实 DOM
上去,这样咱们就减小了对真实 DOM
的操做。
在React
中更新虚拟DOM
的速度更快,由于React
使用了
observable
)而不是脏检查来检测更改AngularJS
使用脏检查来查找已更改的模型。这个脏检查过程在指定时间后循环运行。随着应用程序的增加,检查整个模型会下降性能,从而使应用程序变慢。
每当调用setState()
方法时,ReactJS
都会从头开始建立整个Virtual DOM
。建立整棵树很是快,所以不会影响性能。
在任何给定时间,ReactJS
维护两个Virtual DOM
,一个具备更新的状态Virtual DOM
,另外一个具备先前的状态Virtual DOM
。
使用diff算法比较Virtual DOM
以找到并更新至Real DOM
。
让咱们举个栗子,首先咱们 state.subject
的值是 world
:
<div>
<div id="header">
<h1>Hello, {{state.subject}}!</h1>
<p>How are you today?</p>
</div>
</div>
复制代码
解析后的 Virtual DOM
能够表示为:
{
tag: 'div',
children: [
{
tag: 'div',
attributes: {
id: 'header'
},
children: [
{
tag: 'h1',
children: 'Hello, World!'
},
{
tag: 'p',
children: 'How are you today?'
}
]
}
]
}
复制代码
如今, state.subject
的值改变为Mom
,那么渲染出来的 Virtual DOM
为:
{
tag: 'div',
children: [
{
tag: 'div',
attributes: {
id: 'header'
},
children: [
{
tag: 'h1',
children: 'Hello, Mom!'
},
{
tag: 'p',
children: 'How are you today?'
}
]
}
]
}
复制代码
经过 diff 算法以后,肯定只更新的了 h1
这个元素,那么将更新的元素再映射到 DOM
上即完成了这次的更新。
至于 batching
和 diff 算法
,内容量较大,须要另开一篇博客讲,目前能搜到的讲解也很多,你们能够去搜搜。
关于Virtual DOM
的每一篇文章和文章都会指出,虽然今天的JavaScript
引擎速度很是快,但读取和写入浏览器的DOM
的速度很慢。
这不彻底正确。DOM
很快。添加和删除DOM
节点并不比在JavaScript
对象上设置属性慢得多。这只是一个简单的操做。
例以下面一个例子:
这是一个使用原生 DOM
渲染的方式:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello JavaScript!</title>
</head>
<body>
<div id="example"></div>
<script> document.getElementById("example").innerHTML = "<h1>Hello, world!</h1>"; </script>
</body>
</html>
复制代码
这是一个使用 React
实现的方式:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="build/react.js"></script>
<script src="build/react-dom.js"></script>
</script>
</head>
<body>
<div id="example"></div>
<script type="text/babel"> ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') ); </script>
</body>
</html>
复制代码
使用原生须要渲染的时间:
使用 React
须要渲染的时间:
咋一看,原生渲染速度大大快于 React
。
可是咱们忽略了一个问题,就是页面数据量不多,这样操做,在一个大型列表全部数据都变了的状况下,还算是合理,可是,当只有一行数据发生变化时,它也须要重置整个 innerHTML
,这时候显然就形成了大量浪费。
比较 innerHTML
和 Virtual DOM
的重绘过程以下:
innerHTML
: render html string ==> 从新建立全部 DOM 元素Virtual DOM
: render Virtual DOM ==> diff ==> 必要的 DOM 更新和 DOM
操做比起来,js
计算是很是廉价的。Virtual DOM render
+ diff
显然比渲染 html
字符串要慢,可是,它依然是纯 js
层面的计算,比起后面的 DOM
操做来讲,依然好了太多。
浏览器在DOM
更改时必须执行的布局。每次DOM
更改时,浏览器都须要从新计算CSS
,进行布局并从新绘制网页,这须要大量时间。
浏览器制造商不断努力缩短从新绘制屏幕所需的时间,能够作的最大的事情是最小化和批量DOM
更改。
这种减小和批处理DOM
更改的策略,采用另外一个抽象级别,是React
的Virtual DOM
背后的理念。
React
历来没有说过 “React
比原生操做 DOM
快”。React
给咱们的保证是,在不须要手动优化的状况下,它依然能够给咱们提供过得去的性能。
React
掩盖了底层的 DOM
操做,能够用更声明式的方式来描述咱们目的,从而让代码更容易维护。
借鉴了知乎上的回答:没有任何框架能够比纯手动的优化 DOM
操做更快,由于框架的 DOM
操做层须要应对任何上层 API
可能产生的操做,它的实现必须是普适的。针对任何一个 benchmark
,我均可以写出比任何框架更快的手动优化,可是那有什么意义呢?在构建一个实际应用的时候,你难道为每个地方都去作手动优化吗?出于可维护性的考虑,这显然不可能。
最后推广一下我基于 Taro
框架写的组件库:MP-ColorUI。
能够顺手 star 一下我就很开心啦,谢谢你们。