參考連結:https://juejin.im/post/59663eaa6fb9a06ba73d4c35
閱讀這篇文章後,發現前端模板引擎的實現其實也並不是很難,我們現在一步步來分析。
1、原始拼接html片段:
var author = 'me';
var html = `<p>my name is ${author}</p>`; 複製程式碼
缺點:手動拼接字串比較容易出錯,並且瀏覽拼接速度較慢。
2、既然容易出錯,拼接慢,是否有另一種替代方案呢?答案就是陣列轉字串,join()方法
var author = 'me';
var result = ['<p> my name is ', author, '</p>'].join('');複製程式碼
下面根據第二種方案來解決模板問題:例如
// 模板編譯函式
function TemplateEgine(tpl, data) {
}
//
function Test() {
this.id = 'test';
this.data = {
name: 123465
}
this.result = [];
}
var tpl = '<p>my name is <% id %> </p>';
var test = new Test();
TemplateEgine(tpl, test.id);
// 分析
1)對於這種單一變數的替換,自然想到使用正規表示式來替換掉佔位符的內容,然後替換真正的值,結果為:
result.push('<p> my name is');
result.push(test.id);
result.push('</p>');
但是如果變數是巢狀呢?例如:
var tpl = '<p><% id %>my name is <% data.name %> </p>';
var test = new Test();
TemplateEgine(tpl, {
id: test.id,
data: test.data
});
我們不能再單純替換了,而是應該執行佔位符裡面的程式碼,能夠執行js程式碼的,方法時eval和 new Function(),
一般new Function效能比evel好,這裡採用Function。
還是使用陣列儲存:
result.push(this.id);
result.push('<p> my name is');
result.push(this.data.name);
result.push('</p>');
new Function('return this.result.join("")').call(test);
// 由於 new Function執行是建立一個匿名函式,作用域是全域性的,所以要指定this
2)雖然解決變數的問題,但是如果是遇到複雜的語句,if for 等,如果push到陣列去,不能執行,因此需要根據
正規表示式來判斷是複雜語句還是變數,如果是變數直接替換,如果是複雜語句先執行裡面內容,將執行結果push到
陣列,轉為變數這種情況。例如:
<% for (var i = 0; i < 11; i++) {%>
<p><% i %></p>
<%}%>
結果:
for (var i =0; i < 11; i++) {
result.push('<p>');
result.push(this.i);
result.push('</p>');
}
new Function('return this.result.join("")').call(test);
總結:對於變數直接替換,對於複雜語句先執行,轉為變數這種情況,兩者使用 new Function來執行,由於是全域性
作用域,所以需要繫結this.
區別:vue react模板引擎是帶指令的,會把其轉為AST樹,過程比上面更復雜,待續。。。複製程式碼