现在有各种框架,其中一个主要模块就是关于template。最火的vue、react等框架,在这一块上也是是下足了功夫。我也想写一个自己的模板编译工具,所以就做了个简单的实现,主要是使用es6的反引号编译。
1.选择
这里使用es6的反引号去编译文本节点,只要把数据放在scope中,我们就可以使用反引号加“${}”的方式去把变量替换掉模板中的内容。
2.编译内容
首先,我尝试去直接编译一个文本,让变量能够被填充,模板这么写
<div id="app">
<p>your name is ${name}</p>
<p>your age is ${age}</p>
</div>
模板中${}中的内容是要被替换的。首先我们获取文本字符串
const root = document.querySelector('#app');
let htmlFragment = root.innerHTML;
然后我们要找出需要替换的变量名,这里用正则查找
const templateReg = /\${([^{}])+}/g;
let res = null;
let keyArray = [];
//把找到的变量存起来
while(res = reg.exec(htmlFragment)){
let key = res[0].slice(2,res[0].length-1);
keyArray.push(key);
}
我们在js中定义的数据格式是这样的
let data = {
name:"javascript",
age:"22"
}
接下来,我们把js中的数据格式替换掉模板中的
for(let item of keyArray){
let nReg = new RegExp("\\${"+item+"}","g");
htmlFragment = htmlFragment.replace(nReg, '${data["'+item+'"]}');
}
这里说一下为什么要替换,首先是es6中编译字符串中的变量,这个变量必须在scope中,然后,我们的数据不可能是就像一个一个的变量,那样要定义太多,还有就是模板中尽量写法简洁。所以这里把简洁的变量替换为正真的scope中的变量。因为是要动态替换,所以正则表达式每次都要重新定义(注意转义)。
最后就是如何编译的问题,因为我们拿到的是个字符串,而es6是要用反引号来编译,所以这里就需要拼接反引号和字符串进行编译,大家最容易想到的就是eval,但是介于它可能会带来一些不可预知的东西,所以我们使用new Function()的方式。
let str = new Function("return `"+htmlFragment+"`");
root.innerHTML = "";
//这个方法很久之前就有了,并不是最新出的
root.insertAdjacentHTML('afterbegin',str);
3.编译模板
这里借鉴vue的节点编译方式,主要就是对元素节点和文本节点的处理。由于并没有做循环渲染,所以目前只做了对文本节点的处理。基本的思想还是以一颗树的形式去一层一层的编译。
class Compile{ constructor(node){ this.compileNode(node); node.hasChildNodes() ? this.compileNodeList(node.childNodes) : null; } compileNodeList(nodeList){ let childListFn, node; for(node of nodeList){ this.compileNode(node); node.hasChildNodes ? this.compileNodeList(node.childNodes) : null; } } compileNode(node){ console.log(node); if(node.nodeType == 1){ this.compileElement(node); }else if(node.nodeType == 3){ this.compileText(node); } } compileElement(node){ //解析指令 } compileText(node){ //解析模板 node.data; node.data = this.compileTemplate(node.data)(); } compileTemplate(textFragment){ let res = null; let keyArray = []; while(res = templateReg.exec(textFragment)){ let key = res[0].slice(2,res[0].length-1); keyArray.push(key); } for(let item of keyArray){ let nReg = new RegExp("\\${"+item+"}","g"); console.log(nReg.test(textFragment)); textFragment = textFragment.replace(nReg, '${data["'+item+'"]}'); } return new Function("return `"+textFragment+"`"); }}//new这个对象即可new Compile(root);
全部的可以去github上看,这个后续也会加强功能。https://github.com/Stevenzwzhai/plugs/tree/master/es6-template