上篇博客,我们得到表达式的内容了,那么接下来我们想干什么?是不是想把挑出来的这段表达式,给它解析了呀?
我们在之前的博客中也有做过,用的是 eval。
首先肯定一点,其实也没什么特别高明的办法,要么 eval,要么就 function,就这两种办法。
不过现在,我们准备重新的再来这一下这个事,为什么呢?
首先,在我们编译 s 这个字符串的过程当中,别的都可以忽略,但起码得有数据吧?
那么这个 VText,它身上有数据吗?并没有。
那谁身上有?是不是 VComponent 身上有,所有的数据都在 VComponent 身上。相当于不管是 VElement,还是 VNode,它们其实都是没有数据的状态,都是一个纯节点。
那么如果想要数据,就得找 VComponent,所以这时候,我们就得知道,这个节点它所对应的那个 component 是谁。
那么我们就直接来了,对于 VText 和 VElement 它们两个来说,我们还需要一个额外的参数。
什么参数呢?那就是我的 component 是谁:
因为如果不知道这个的话,其实是很费劲的。
然后这个 component 需要做几件事:
首先,我们 assert 一下,你必须得给我,你不给我就干不了活,因为数据都没,啥都做不了。
然后我们要把它给存起来:
以及 VElement 这边,我们也需要 component ,因为你得告诉我,我属于谁,然后我在借助它的数据,去编译我的东西:
当然,如果在这时候我们直接就这么运行,是会报错的:
其实就是 VText 这边报的,因为我们并没有传 component,那这个怎么办呢?
非常简单,首先我们别忘了,VText 是谁创建出来的?其实 VText 是 VElement 创建出来的,为什么这么说呢?
因为我们 VElement 里面的 createChildren,它是要递归的往里面创建的,并且它在递归的过程当中,是不是顺便的就把我们的那个 VText 也创建出来了。
所以说白了,它是从 VElement 里面来的。
所以在这种情况下,事情就变的非常的好办,我们就可以在 createChildren 里面,给它加个参数:
并且,分别传给我的子元素 VElement,和子文本元素 VText:
当然这时候又有一个问题了,你可以看到页面上还是报错:
这回的报错,其实是 VElement,因为我们现在并没有 component 这个东西。
如果我们在 VElement 里面也做个 assert,你就可以清楚的看到了。
因为我们往下面传,是没问题的,但是首先,我自己得有这个 component,不然我都没有,怎么往下传呢。
那这时候怎么办,我怎么样才能给它加上呢?其实特别的简单。
大家别忘了,我们的 VComponent 是哪来的?是不是继承自 VElement 的:
并且,在这个 VComponent 里面,它的 super 这里,我们要先搞清楚一个问题,在这,谁是组件?
比如现在,我想调父级,这里的 super 对应的是不是就是 VElement?
那谁是组件?
其实就是我自己,VComponent 它自己就是组件。
那么在这种情况下,我们就可以这么来写。
但是,你仍然可以看到页面上的报错:
它说,你在调用 super 之前,不能用 this,这就是一个鸡生蛋还是蛋生鸡的问题。
所以,现在这里不能放 this,因为这个时候,super 还没执行完:
但是我创建的时候,确实又需要 this,那怎么办呢?
其实我们可以用一个特别简单的办法来解决这个问题。
现在只有一种人,是无须提供 component,就能直接用的,这种人是谁?
这种人是谁?就是我自己,我自己就是 component。
所以我们 VElement 这里就不做 assert 了,因为我自己,我这个VElement,有可能就是一个 component:
那我怎么知道我是不是 component?
很简单,那就得看看,是不是继承出来的。
然后这个 component 有可能是空的,那如果是空的,我就用 this,因为我是一个 component:
那么你可以看到,这个时候,页面上就没有报错了:
所以我们这个 component 的引用就算是好了。
然后我们把 cmp 打印出来看看,看看它的子级:
你可以看到,第一个子级是个 VElement,并且,它里面有一个组件 _component。
这个组件是谁?你可以看到,就是我的父级,div#root。
包括下面的 VText 和 VElement,它们的 _component 也都是 div#root:
所以相当于现在,我是在用我未来的值,有点类似于高阶类。
实际上来说,我自己有可能是个 VComponent,那我就用自己就好了。
当然,如果我提供了 component 就用,没提供就用我自己,说明我自己就是顶层。
因为我自己是没有父级的,就这么简单。
这个东西稍微有点绕,首先,我们的最外层就是一个 VComponent,因为我们写的就是 let cmp = new VComponent({...})。
其实说白了,所有人引的都是最外层的它。
当然这个所有人,也包括它自己,它自己的 component 引用,就是它自己。
那为什么会有这样的一个状态呢?很简单。
你别看我是 VComponent,没错,确实有点特殊,但我也是一个 VElement,因为 VComponent extends VElement。
就是说,首先我是一个 VElement,然后其次我才是一个 VComponent。
所以这里面稍微有一点容易乱的地方就在于:不光最外层它里面的那些东西 VElement、VText,它们都需要引我,这个大家肯定能理解,还有一点就是,我自己也要引我自己。
说的直白一点,我们写的 let cmp = new VComponent({...}) 的这个 VComponent,它对应的其实就是最外层的 div:
根节点对应着根组件,很正常。
所以在这里面有一个问题,就是我自己的父级,就是我自己,因为已经到头了。
所以,除了我下面的那些子元素得找我,因为它们自己没有数据,我有,所以它们肯定得找我。
然后除了它们找我之外,我自己也要找我自己。
因为我自己,也就是最外层的这个根元素,也需要一些数据,比如像这些:
我也是需要数据的,这没办法。
所以在这种情况下,这个 component,它有可能有值:
为什么可能有值呢?
因为我们在 createChildren 的时候,会传一个 component:
我们有可能传值了,也有可能没传值。
所以在下面,理论上我们就可以做一个判断,如果 component 是有值的,那么 this._component 就是 component:
这个相信大家绝对能理解,但是不太好理解的就是 else。
else 如果没有值,说明我就是顶层,所以才没人给我。
所以在这时候,既然我就是顶级,那我就可以断定,我自己是个 VComponent。
我们可以来试试,在 else 里面我们来 console 一下:
这个时候,你可以看到,就是 true。
当然,在别的级别里面,比如 VElement 和 VText,如果我们也 console 的话,看看它们是不是 VComponent:
你可以看到,都是 false,就我一个人是 true。
所以说白了,有一个元素是很特殊的,就是最外层的那个元素 div#root。
那么它特殊在哪?特殊在它没有父级,它自己就是那个根,这就意味着它就有两重身份,它既是一个 VElement 标签元素,同时它也是一个 VComponent 组件。
当然,为什么它是个 VComponent 组件,因为我们在里面的 super 会执行到 VElement 那去。
所以说白了,我们在 VElement 这里就要判断一下,有人给我传,那挺好。
没人给我传怎么办?那我就要自己解决。
所以在 else 的时候,我的 _component,就是我自己:
当然,把它作一个简写,就是下面的这个东西:
相信大家现在就能理解了。
那么接下来,我们在 VText 的编译过程当中,其实就已经不光是有这个 s 了,并且还有数据了。
那么这个数据从哪拿?就是从 this._component 里面的 _data:
那么现在这时候你可以看到,比如第一个 a,当我要编译 a 的时候,是不是后面的数据就已经给我了呀,下面也都是一样。
既然有了数据,那么我们是不是就可以通过 this._component._data[s] 来做?当然这样是没有办法适应表达式的,但没关系,我们知道怎么做就行,一步步来。
然后我们是不是应该把 this._component._data[s] 这个值给存起来,并且让它拿去替换掉它原来的那个页面里的那堆东西。
以前的博客中,我们是用 replace 做的,而现在我们没用 replace,所以这个工作我们就需要自己来。
当然这又是另一个工作了,这些其他的部分,怎么给它拧出来,也是需要去做的。
所以目前来说,后面就都是一些劳动量的问题了,最主要的问题,我们已经解决完了。