正文
前言
目前最流行的两大前端框架,React和Vue,都不约而同的借助Virtual DOM技术提高页面的渲染效率。那么,什么是Virtual DOM?它是通过什么方式去提升页面渲染效率的呢?本系列文章会详细讲解Virtual DOM的创建过程,并实现一个简单的Diff算法来更新页面。本文的内容脱离于任何的前端框架,只讲最纯粹的Virtual DOM。敲单词太累了,下文Virtual DOM一律用VD表示。
这是VD系列文章的开篇,以下是本系列其它文章的传送门:
-
你不知道的Virtual DOM(一):Virtual Dom介绍
-
你不知道的Virtual DOM(二):Virtual Dom的更新
-
你不知道的Virtual DOM(三):Virtual Dom更新优化
-
你不知道的Virtual DOM(四):key的作用
-
你不知道的Virtual DOM(五):自定义组件
-
你不知道的Virtual DOM(六):事件处理&异步更新
PS:系列文章可点击“
阅读全文
”查看。
VD是什么
本质上来说,VD只是一个简单的JS对象,并且最少包含tag、props和children三个属性。不同的框架对这三个属性的命名会有点差别,但表达的意思是一致的。它们分别是标签名(tag)、属性(props)和子元素对象(children)。下面是一个典型的VD对象例子:
{
tag: "div",
props: {},
children: [
"Hello World",
{
tag: "ul",
props: {},
children: [{
tag: "li",
props: {
id: 1
,
class: "li-1"
},
children: ["第", 1]
}]
}
]
}
VD跟dom对象有一一对应的关系,上面的VD是由以下的HTML生成的:
Hello World
id="1" class="li-1">
第1
一个dom对象,比如
li
,由
tag
(
li
)
,
props
({
id
:
1
,
class
:
"li-1"
})
和
children
([
"第"
,
1
])
三个属性来描述。
为什么需要VD
借助VD,可以达到有效减少页面渲染次数的目的,从而提高渲染效率。我们先来看下页面的更新一般会经过几个阶段。
从上面的例子中,可以看出页面的呈现会分以下3个阶段:
如何实现VD与真实DOM的映射
我们先从如何生成VD说起。借助JSX编译器,可以将文件中的HTML转化成函数的形式,然后再利用这个函数生成VD。看下面这个例子:
function render() {
return (
<div>
Hello World
<ul>
<li id="1" class="li-1">
第1
li>
ul>
div>
);
}
这个函数经过JSX编译后,会输出下面的内容:
function render() {
return h(
'div',
null,
'Hello World',
h(
'ul',
null,
h(
'li',
{ id: '1', 'class': 'li-1' },
'\u7B2C1'
)
)
);
}
这里的h是一个函数,可以起任意的名字。这个名字通过babel进行配置:
// .babelrc文件
{
"plugins": [
["transform-react-jsx", {
"pragma":
"h" // 这里可配置任意的名称
}]
]
}
接下来,我们只需要定义h函数,就能构造出VD:
function flatten(arr) {
return [].concat.apply([], arr);
}
function h(tag, props, ...children) {
return {
tag,
props: props || {},
children: flatten(children) || []
};
}
h函数会传入三个或以上的参数,前两个参数一个是标签名,一个是属性对象,从第三个参数开始的其它参数都是children。children元素有可能是数组的形式,需要将数组解构一层。比如:
function render() {
return (
<ul>
<li>0li>
{
[1, 2, 3].map( i => (
<li>{i}li>
))
}
ul>
);
}
// JSX编译后
function render() {
return h(
'ul',
null,
h(
'li',
null,
'0'
),
/*
* 需要将下面这个数组解构出来再放到children数组中
*/
[1, 2, 3].map(i => h(
'li',
null,
i
))
);
}
继续之前的例子。执行h函数后,最终会得到如下的VD对象:
{
tag: "div",
props: {},
children: [
"Hello World",
{
tag: "ul",
props: {},
children: [{
tag