手撕Vue-编译模板数据
经上一篇编译指令数据后,我们已经可以将指令数据编译成具体需要展示的数据了,上一篇只是编译了指令数据,还没有编译模板数据,这一篇我们就来编译模板数据。
也就是 {{}}
这种模板的形式我们该如何编译,其实和指令数据编译的思路是一样的,废话不多说,直接上代码。
改造一下 buildText 方法,让它支持编译模板数据,调用 CompilerUtil content 方法,传入模板数据,返回编译后的数据,然后再将编译后的数据替换到文本节点中。
CompilerUtil['content'](node, content, this.vm);
在 CompilerUtil 中添加 content 方法,该方法和指令数据编译的思路是一样的,只是编译的数据不一样,指令数据是 v-text
这种,而模板数据是 {{}}
这种。
/**
* 处理模板字符串
* @param node 当前元素
* @param value 指令的值
* @param vm Nue 的实例对象
*/
content: function (node, value, vm) {
// console.log(value); // {{ name }} -> name -> $data[name]
node.textContent = this.getContent(vm, value);
}
getContent 方法我单独拿出来,我先贴代码,然后再解释。
/**
* 获取指定模板字符串的内容
* @param vm Nue 的实例对象
* @param value 指令的值
*/
getContent(vm, value) {
const reg = /\{\{(.+?)\}\}/gi;
return value.replace(reg, (...args) => {
// 第一次执行 args[1] = name
// 第二次执行 args[1] = age
return this.getValue(vm, args[1]);
});
},
getContent 方法中,我们先定义了一个正则表达式,用来匹配模板字符串,/\{\{(.+?)\}\}/gi
这个正则表达式的意思是匹配 {{}}
里面的内容,g
表示全局匹配,i
表示忽略大小写,(.+?)
表示匹配任意字符,+
表示匹配一次或多次,?
表示非贪婪模式,也就是匹配到第一个 }}
就结束匹配。()
表示分组,args[1]
就是分组匹配到的内容。
在方法当中,我们调用了 getValue 方法,该方法的作用是获取模板字符串的值,在运行测试代码的时候,我发现, {{ name }}
这种模板编译出来是 undefined 所以我们需要在 getValue [obj][key]
的时候,去一下空格:
return data[currentKey.trim()];
这样就可以了,我们再来看一下测试代码:
<p>{{ name }}</p>
<p>{{age}}</p>
<p>{{time.h}}</p>
<p>{{name}}-{{age}}</p>
发现 {{name}}-{{age}}
这种也可以编译了,那么可以我们来看看我们 getContent 源码的执行效果,如果是 {{name}}-{{age}}
, value 等于 {{name}}-{{age}}
进入 replace 循环,第一次执行 args[1]
等于 name
,第二次执行 args[1]
等于 age
,第一次循环已经将我们的 {{name}}-{{age}}
替换为了 BNTang-{{age}}
,第二次循环将 {{age}}
替换为了 age
,所以最终的结果就是 BNTang-33
。