<待更新>如何实现一个Virtual Dom(一)

生成Dom树

假设有以下Dom结构

1
2
3
4
<ul>
<li>item 1</li>
<li>item 2</li>
</ul>

则对应的JS对象应该为

1
2
3
4
5
6
7
8
9
10
{
type: 'ul',
props: {
class: 'list',
children: [
{ type: 'li', props: {}, children: ['item 1']},
{ type: 'li', props: {}, children: ['item 2']}
]
}
}

编写一个辅助函数h,上面的机构更加直观点

1
2
3
function h (type, props, ...children) {
return { type, props, children }
}

通过h函数上面的结构就变成了下面这种

1
2
3
4
5
h(
'ul', { 'class': 'list' },
h('li', {}, 'item 1'),
h('li', {}, 'item 2')
)

借助BabelJSX优化。BabelJSX可以对JSX代码进行转换,会将以下代码:

1
2
3
4
<ul className="list">
<li>item 1</li>
<li>item 2</li>
</ul>

转换成

1
2
3
4
React.createElement('ul', { className: 'list' },
React.createElement('li', {}, 'item 1'),
React.createElement('li', {}, 'item 2')
)

将React.createElement换成函数h就可以通过h函数进行转换,代码如下(要设置编辑器环境和顶部/** @jsx h */):

控制台输出的结果为:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
children: [{
children: ["item 1"],
props: null,
type: "li"
}, {
children: ["item 2"],
props: null,
type: "li"
}],
props: null,
type: "ul"
}

根据Dom树创建真实Dom

先不考虑props和children属性来创建一个createElement函数

1
2
3
4
5
6
function createElement(node) {
if (typeof node === 'string') {
return document.createTextNode(node)
}
return document.createElement(node.type)
}

上面代码再只考虑文本节点和元素节点时是可以使用的,但当子元素还是文本节点或者元素时,就需要使用递归来循环调用,下面是实现代码:

处理Dom节点的变更

转换成真实Dom后,会出现