(译文)如何实现virtual dom

创建虚拟dom需要知道两件事。你不需要看深入研究React的源码。或者其它虚拟dom的源码。他们都非常繁琐也复杂。实际上虚拟dom只需要50行以内的代码就可以实现!!!

这里有两个概念:

  • 虚拟dom是真实dom的一种呈现方式
  • 当我们改虚拟dom树,我们得到一个新的dom树,有一个算法会查找新旧dom树的差别,并且在实际dom上做最小的改变 就是这样,现在我们开始深入研究下这两个概念。

更新:本文的第二部分:虚拟dom 设置props&events here

#DOM树的呈现
首先我们需要在内存中存储DOM树,并且我们能使用js对象来表示,假设我们有以下的树:

1
2
3
4
<ul class=”list”>
<li>item 1</li>
<li>item 2</li>
</ul>

怎样看起来更简单些呢?我们如何才能将上述结构转成下面的这种js对象呢?

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

这里有零点需要注意:

  • 使用object来表示dom元素
  • text用js字符串表示

但是使用这种方式来写一个大型的树是非常难得,所以我们来写一个帮助函数,这个会帮助我们更好的理解代码结构

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

现在我们可以这样写我们的dom

1
2
3
4
h(‘ul’, { ‘class’: ‘list’ },
h(‘li’, {}, ‘item 1’),
h(‘li’, {}, ‘item 2’),
);

这样看起来更加清晰了,但是我们可以再深入些,听说过jsx吧,所以他是如何工作的呢?

如果你阅读过jsx的官方文档,你就知道,babel会将如下代码怎么编译

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

to

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

看起来是不是很相似?是的,假设我们能把React.createElement替换为h,那么我们就可以使用jsx编译语法,我们只需在文件的顶行添加如下注释:

1
2
3
4
5
/** @jsx h */
<ul className=”list”>
<li>item 1</li>
<li>item 2</li>
</ul>

这实际上就是告诉babel “hey,编译这个jsx但是使用h 替换React.createElement”,这里你可以使用任何字符串放在h这个位置,它都会被编译

所以,总结下,我们可以这样写dom

1
2
3
4
5
6
7
/** @jsx h */
const a = (
<ul className=”list”>
<li>item 1</li>
<li>item 2</li>
</ul>
);

它将被babel转成下面这样:

1
2
3
4
5
6
const a = (
h(‘ul’, { className: ‘list’ },
h(‘li’, {}, ‘item 1’),
h(‘li’, {}, ‘item 2’),
);
);

当’h’执行时,将会返回一个js对象(虚拟dom的呈现)

1
2
3
4
5
6
const a = (
{ type: ‘ul’, props: { className: ‘list’ }, children: [
{ type: ‘li’, props: {}, children: [‘item 1’] },
{ type: ‘li’, props: {}, children: [‘item 2’] }
] }
);