创建虚拟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
| 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’] } ] } );
|