9
10
11
12
34
35
复制代码
我们现在来更改index.js入口文件的代码,这里特别说一下其中的$mount方法,他试试是要做一个这样的事情:
复制代码
//模板字符串
{{message}}
复制代码
复制代码
//render函数
function anonymous() {
with(this){return _h('div',{attrs:{"id":"app"}},["\n "+_s(message)+"\n"])}
}
复制代码
将模板转换为一个函数render放到参数上,这里我们先简单实现,后续深入后我们重新翻下这个函数,修改后我们的index.js变成了这个样子:
index.js
这里仅仅是到输出vnode这步,接下来是将vnode转换为函数render,在写这段代码之前我们来说一说Vue中的render参数,事实上,我们new Vue的时候可以直接传递render参数:
复制代码
1 new Vue({
2 render: function () {
3 return this._h('div', {
4 attrs:{
5 a: 'aaa'
6 }
7 }, [
8 this._h('div')
9 ])
10 }
11 })
复制代码
他对应的这段代码:
复制代码
1 new Vue({
2 template: 'Hello World!
'
3 })
复制代码
真实代码过程中的过程,以及我们上面代码的过程是,template 字符串 => 虚拟DOM对象 ast => 根据ast生成render函数......,这里又涉及到了另一个需要引用的工具库snabbdom
snabbdom-render
https://github.com/snabbdom/snabbdom,Vue2.0底层借鉴了snabdom,我们这里先重点介绍他的h函数,h(help帮助创建vnode)函数可以让我们轻松创建vnode,这里再对Virtual DOM做一个说明,这段话是我看到觉得很好的解释的话(https://github.com/livoras/blog/issues/13):
我们一段js对象可以很容易的翻译为一段HTML代码:
复制代码
1 var element = {
2 tagName: 'ul', // 节点标签名
3 props: { // DOM的属性,用一个对象存储键值对
4 id: 'list'
5 },
6 children: [ // 该节点的子节点
7 {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
8 {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
9 {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
10 ]
11 }
复制代码
复制代码
1 -
2
- Item 1 3
- Item 2 4
- Item 3 5
2
7
复制代码
_h('div',{},[_h('div',{},["\n "+_s(name)]),_h('span',{},[_s(age+1)]),_h('input',{}),_h('br',{})])
真实运行的时候这段代码是这个样子的:
这段代码很纯粹,不包含属性和class,我们只需要处理文本内容替换即可,今天的任务比较简单,所以接下来的流程后便可以得出第一阶段代码:
复制代码
1
2
3
4
3 {{name}}
4 {{age+1}}
5
6 7
9
10
11
12
36
37
复制代码
libs/index.js
之前我们图简单,一直没有解决属性问题,现在我们在模板里面加入一些属性:
复制代码
1
2
7
复制代码
情况就变得有所不同了,这里多加一句:
复制代码
1 setElAttrs(el, delimiters)
2 //==>
3 function setElAttrs(el, delimiters) {
4 var s = delimiters[0], e = delimiters[1];
5 var reg = new RegExp(`^${s}(\.+\)${e}$`);
6 var attrs = el.attrsMap;
7 for (let key in attrs) {
8 let value = attrs[key];
9 var match = value.match(reg)
10 if (match) {
11 value = match[1];
12 if (isAttr(key)) {
13 el.props[key] = '_s('+value+')';
14 } else {
15 el.attrs[key] = value;
16 }
17 } else {
18 if (isAttr(key)) {
19 el.props[key] = "'" + value + "'";
20 } else {
21 el.attrs[key] = "'" + value + "'";
22 }
23 }
24
25 }
26 }
复制代码
这段代码会处理所有的属性,如果是属性中包含“{{}}”关键词,便会替换,不是我们的属性便放到attrs中,是的就放到props中,这里暂时不太能区分为什么要分为attrs何props,后续我们这边给出代码,于是我们的index.js变成了这个样子:
libs/index.js
复制代码
_h('div',{attrs:{"data-name":name,"data-flag":'start',"ontap":'clickHandler'},props:{"class":'c-row search-line'}},
[_h('div',{props:{"class":'c-span9 js-start search-line-txt'}},
["\n "+_s(name)]),_h('span',{},
[_s(age+1)]),_h('input',{props:{"type":'text',"value":_s(age)}}),_h('br',{})])
复制代码
复制代码
1
3 {{name}}
4 {{age+1}}
5
6 7
2
复制代码
然后我们来处理class以及style,他们是需要特殊处理的:
复制代码
3
8
9
4 叶小钗
5 31
6
7 8
{{name}}
{{age+1}}
2
7
复制代码
虽然这段代码能运行,无论如何我们的属性和class也展示出来了,但是问题却不少:
① 这段代码仅仅就是为了运行,或者说帮助我们理解
② libs/index.js代码已经超过了500行,维护起来有点困难了,连我自己都有时候找不到东西,所以我们该分拆文件了
于是,我们暂且忍受这段说明性(演示性)代码,将之进行文件分拆
文件分拆
文件拆分后代码顺便传到了github上:https://github.com/yexiaochai/wxdemo/tree/master/mvvm
这里简单的解释下各个文件是干撒的:
复制代码
1 ./libs
2 ..../codegen.js 代码生成器,传入一个ast(js树对象),转换为render函数
3 ..../helps.js 处理vnode的相关工具函数,比如处理属性节点,里面的生成函数感觉该放到utils中
4 ..../html-parser.js 第三方库,HTML解析神器,帮助生成js dom树对象
5 ..../instance.js 初始化mvvm实例工具类
6 ..../mvvm.js 入口函数
7 ..../parser.js 模板解析生成render函数,核心
8 ..../text-parser.js 工具类,将{{}}做替换生成字符串
9 ..../utils.js 工具库
10 ..../vnode.js 虚拟树库,暂时自己写的,后续要换成snabbdom
11 ./index.html 入口文件
复制代码https://www.cnblogs.com/yexiaochai/p/9699698.html
3 叶小钗
4 31
5
6 7
