经过前面的几篇博客,相信到现在为止,一些 DOM 的基本操作是没问题的,那么接下来就要做点事了,我们来尝试着去编译真实的 DOM 节点。
什么叫编译?很简单。
第一,我要把这个真实存在的元素,变成一个我自己的虚拟 DOM 元素。
第二,我要把这个元素身上,跟我相关的东西,都拿出来。
假设现在以 vue 为例,对于 vue 来说,你上面加个 title,人家关心吗?
不关心。人家关心的,其实是跟我有关系的东西。
你比方说我加了个 v-if,或者 @click:
这个我关心。
说白了,就是我在编译的过程当中,我要把跟我有关的,有功能的,这些自定义的属性,给它拎出来,将来我要用。
那么我们现在就直接来看一看,怎么来编译 DOM 节点。
当然,仅仅只算是一个初级版本,因为这个事可大可小,它未来其实会有很多复杂的东西。
所以我们现在要做的是一个初级的版本,所以也不会特别难。
那么现在,我希望是这样的,比方说,我有一个姓名 name:
当然顺便一说,我一定得用这个双花括号 {{ }} 吗?一定得跟着 vue 走吗?
不一定,我可以用任何我想的东西,反正是我们自己写的。
比方说,我就想用 % 间隔,行吗?
随便,爱用啥用啥。
只是双花括号 {{ }} 我们用习惯了,所以暂时还是这个。
然后继续,比方说,我还希望这里面可以给我做一些简单的运算:
这个要求也不复杂。
以及呢,我还希望这里面可能有一些按钮之类的东西,当我点击这个按钮的时候,这个 a 可以给我++
当然我们先一步一步的来,首先,我们想要来编译这个元素:
那么这个时候,第一件事,我们是不是要把它里面,所有的这些,所谓的模版,我要把它给挑出来啊?
也就是说,我关心的是 {{ 开头,}} 结尾,我关心的是这些东西。
那么接下来,我们要怎么做呢,我们一步一步来。
首先,你如果想把这个东西做的好一点,我们肯定是把它封成一个 class:
然后接下来呢,这里面需要一个 options,而且默认的时候,让它是一个空 json:
然后接下来先不管别的,我们先看看这里面具体想干点什么。
别的可能还不是特别关心,至少,我得有个 render 吧:
那么这个 render 具体怎么做是重点,我们一步一步来。
首先,为了写起来方便,我们把 js 这块单独的拎走,不然写起来太乱了。
这样的话,我们可以左右对照,看起来也方便一些。
然后,我们这个东西,先不管里面怎么写,我就想看看怎么用。
比方说,我现在就来 new 一个,然后这里面会有一些小小的参数,比如我有一个 root:
然后我们除了需要根元素以外,是不是还需要数据啊?
然后开始的第一个事,我希望可以有一个 assert,这个我们前面已经用过很多次了,熟的很。
那么这个 assert 有了之后,我需要检查很多东西。
首先,我们是不是需要根据 damu 所提供的 root 参数,来具体的把根元素给选出来啊。
那么这时候,是不是就可以跟以前一样,我们单独搞一个小操作,比如 _getElement:
我要的很简单,首先,我需要 assert 一下,参数 obj 你必须得有,没有就报错。
然后接下来,我们就来稍微的判断一下,如果它是 string,我们就自己来选,并且还得在判断一下,这东西有没有。
而反过来,如果它是一个元素,是的话就直接用,如果还不是的话,我就报错。
这个东西我们前面也写了很多遍,太熟了,就直接来了:
现在已经有了_getElement,那么我们就可以得到 _root 了:
然后这个有了之后,还没完,我们还需要用一个特别重要的东西,就是我得让 options.data 变成可响应的状态。
正经的情况下,我们需要各种判断,它是不是一个函数,怎么怎么着。
在这,我们就一切从简,反正这些事,我们前面都做过,所以这里就不再去重复了。
然后这个 data 已经有了,那么剩下的事怎么办?
是不是就可以创建一个 proxy 对象,因为我要监听它里面的东西:
这里我们也可以直接把 options.data 给丢进去,合成一步。
然后接下来,我这里面需要各种各样的东西,比如 get 和 set:
再接下来呢,我们的 render 里面是不是也要开始做事了啊?
到这里为止,你可以发现上面很多的操作都没有细说,因为我们前面做过很多次了,如果不是很懂,可以看下前面的博客。
那么到现在,我现在这个 proxy 确实是有了,我们就来看看:
可以看到,我们的实例 damu 身上并没有 a,为什么呀?原因非常简单。
注意,我们在这要区分一个东西,proxy 和 this:
我们在外面这么去写:damu.a
请问这时候,我们是从谁上面去拿 a 的?其实是从 this 身上去拿的。
那么我们 this 身上有没有 a 这个东西?并没有。
谁身上有?是不是 proxy 身上有?那这时候怎么办呢?
我们能写 this = proxy 吗?这么写肯定报错的:
那有没有别的办法呢?有。
在这我们就要说一个及其另类的方法。
比方说我有一个类:
可以看到,现在一切没问题。
但是,在 js 里面,有一个很变态的特性,就是 constructor 是可以有返回值的。
比如我现在返回一个 json:
那么你可以看到,我现在得到的这个实例 a,就是这个 json。
换句话说,在其他语言里面,比如 java,constructor 其实是不能有 return,不能有返回值的,它的返回值就是刚构造好的那个东西,不能是别的。
而 js 当中,它可以 return 一个东西,只要你愿意,我 return 一个任意的对象都行,比如:
那么回到正题,这时候,在外面我们希望设置数据的时候,能够往 proxy 身上设。
所以,我们是不是就可以直接把 proxy 给 return 出去:
既然 return 了 proxy,那相当于我自己的 this 就废了。
当然这时候有人可能会说,那你这 this 身上的 _root 和 timer 不也没了吗?
其实这个 this 其实还是存在的,比方说 set 里面,我们依然调的是 this.render()。
所以说我的 this 并没有消失,只不过我是推出去一个顶雷的:有设置数据的 proxy 去,你顶上。
那么现在,我们来试试:
现在就可以看到,damu 上面是有 a 的了。
然后我们还想赋个值:
这时候就可以看到报错了,为什么会报错?
因为在 set 里面,this 指的就是 proxy。
所以我们需要稍微的变一变:
这时候,render 就出来了。
当然,这个并不是唯一的选择,我们也可以搞一个箭头函数:
可以看到,也是能出来的。
当然,我个人与其说把大量的函数都写成这种箭头函数,我更愿意用 _this。
那么到这为止,我们已经能够让它正确的做出响应了。
但是别忘了,我们的 render 里面到现在为止,什么都没有。
所以,下篇博客我们就开始来干这个事。