現(xiàn)在有各種框架,其中一個主要模塊就是關(guān)于template碰缔。最火的vue账劲、react等框架,在這一塊上也是是下足了功夫金抡。我也想寫一個自己的模板編譯工具瀑焦,所以就做了個簡單的實(shí)現(xiàn),主要是使用es6的反引號編譯梗肝。
1.選擇
這里使用es6的反引號去編譯文本節(jié)點(diǎn)榛瓮,只要把數(shù)據(jù)放在scope中,我們就可以使用反引號加“${}”的方式去把變量替換掉模板中的內(nèi)容巫击。
2.編譯內(nèi)容
首先禀晓,我嘗試去直接編譯一個文本,讓變量能夠被填充坝锰,模板這么寫
<div id="app">
<p>your name is ${name}</p>
<p>your age is ${age}</p>
</div>
模板中${}中的內(nèi)容是要被替換的粹懒。首先我們獲取文本字符串
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中定義的數(shù)據(jù)格式是這樣的
let data = {
name:"javascript",
age:"22"
}
接下來顷级,我們把js中的數(shù)據(jù)格式替換掉模板中的
for(let item of keyArray){
let nReg = new RegExp("\\${"+item+"}","g");
htmlFragment = htmlFragment.replace(nReg, '${data["'+item+'"]}');
}
這里說一下為什么要替換崎淳,首先是es6中編譯字符串中的變量,這個變量必須在scope中愕把,然后拣凹,我們的數(shù)據(jù)不可能是就像一個一個的變量,那樣要定義太多恨豁,還有就是模板中盡量寫法簡潔嚣镜。所以這里把簡潔的變量替換為正真的scope中的變量。因?yàn)槭且獎討B(tài)替換橘蜜,所以正則表達(dá)式每次都要重新定義(注意轉(zhuǎn)義)菊匿。
最后就是如何編譯的問題,因?yàn)槲覀兡玫降氖莻€字符串计福,而es6是要用反引號來編譯跌捆,所以這里就需要拼接反引號和字符串進(jìn)行編譯,大家最容易想到的就是eval象颖,但是介于它可能會帶來一些不可預(yù)知的東西佩厚,所以我們使用new Function()的方式。
let str = new Function("return `"+htmlFragment+"`");
root.innerHTML = "";
//這個方法很久之前就有了说订,并不是最新出的
root.insertAdjacentHTML('afterbegin',str);
3.編譯模板
這里借鑒vue的節(jié)點(diǎn)編譯方式抄瓦,主要就是對元素節(jié)點(diǎn)和文本節(jié)點(diǎn)的處理。由于并沒有做循環(huán)渲染陶冷,所以目前只做了對文本節(jié)點(diǎn)的處理钙姊。基本的思想還是以一顆樹的形式去一層一層的編譯埂伦。
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上看煞额,這個后續(xù)也會加強(qiáng)功能。https://github.com/Stevenzwzhai/plugs/tree/master/es6-template