内容提要
- 虚拟DOM是什么
- 为什么需要虚拟DOM
- jsx语法糖,实现虚拟DOM
- Diff算法
- render 成真实DOM
一、虚拟DOM是什么
vdom可以看作是一个使用javascript模拟了DOM结构的树形结构,这个树结构包含整个DOM结构的信息,例如:
<button class="btn btn-blue">
<em>Confirm</em>
</button>
复制代码
上边的DOM结构,都在下面的树结构里面
{
type: 'button',
props: {
className: 'btn btn-blue',
chidren: [
{
type: 'em',
props: {
chidren: 'Confirm'
}
}
]
}
}
复制代码
二、为什么需要虚拟DOM
这个得从浏览器渲染一个html 文件需要做哪些事说起
这里主要说明浏览器渲染的过程,包括5步
- 浏览器将获取的html文档解析成DOM树
- 处理css,构成层叠样式表模型CSSOM(CSS Object Model)
- 将上面的DOM树和样式表,关联起来,构建一颗Render树。这一过程又称为Attachment。每个DOM节点都有attach方法,接受样式信息,返回一个render对象(又名renderer)。这些render对象最终会被构建成一颗Render树。
- 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次pass绘制操作就可以布局所有的元素。
- 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting.
当你用传统的源生api或jQuery去操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。比如当你在一次操作时,需要更新10个DOM节点,理想状态是一次性构建完DOM树,再执行后续操作。但浏览器没这么智能,收到第一个更新DOM请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程。显然例如计算DOM节点的坐标值等都是白白浪费性能,可能这次计算完,紧接着的下一个DOM更新请求,这个节点的坐标值就变了,前面的一次计算是无用功。
虚拟DOM就是为了解决这个浏览器性能问题而被设计出来的。例如前面的例子,假如一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,通知浏览器去执行绘制工作,这样可以避免大量的无谓的计算量。
三、jsx语法糖,实现虚拟DOM
有 JSX 的地方,在文件开头就需要引入 React,因为实际上 JSX 是使用了 React.createElement,JSX 只是一个JS 的语法糖,所以需要引入 React 包,否则会报错。
// 方法3:JSX
import React from 'react';
import ReactDOM from 'react-dom';
const title = (
<h1>Hello React (method 3)</h1>
);
ReactDOM.render(title, document.getElementById('root'));
复制代码
这段jsx代码,实际是 React.createElement 创建一个 React DOM 对象,完全等同于下面这行代码。
const title = React.createElement("h1", {className: "main"}, "Hello React (method 3)");
复制代码
四、Diff算法
缘由:计算一颗树形结构转换成另外一颗树形结构的最少操作
传统 diff 算法通过循环递归对节点进行依次对比,效率低下,算法复杂度达到 O(n^3),其中 n 是树中节点的总数。O(n^3) 到底有多可怕,这意味着如果要展示1000个节点,就要依次执行上十亿次的比较。代价太高。