ES6 開始支持模板字符串(Template literals),支持以下的寫法:javascript
`string text ${expression} string text`;
其實在不少模板引擎中,經常會有這樣需求,好比經常使用的 doT,使用相似的語法html
<div>{{=1+2}}</div> // 或者支持循環或者判斷 {{for(var i in it){}}} <span>{{=i}}</span> {{}}}
咱們先來看看一個模板引擎基本的實現須要什麼,先不考慮循環和判斷,只支持變量運算。
打開Babel,輸入java
const a = 1; console.log(`Hi\n${2 + 3}!dk${a}`);
通過Babel
轉義之後,能夠看到express
"use strict"; var a = 1; console.log("Hi\n".concat(2 + 3, "!dk").concat(a));
能夠看到,Babel
把插值提取到 concat 入參,經過函數入參的自計算實現了 Template literals。在咱們的使用中,其實無法直接作到這樣的效果。
可是仿造Babel
的作法,咱們能夠整理一下本身的思路:babel
eval
或者new Function()
實現插值的計算代碼實現以下:函數
var str = "string text ${1 + 2} string text ${2 + 3} test"; function template(str) { var pattern = /\$\{.*?\}/g; var patternCapture = /\$\{(.*?)\}/g; // 將非插值字符串分割出來 var strArr = str.split(pattern); // 將插值字符串分割出來 var rawArr = str .match(patternCapture) .map(item => item.replace(patternCapture, "$1")); // eval轉換 var valueArr = rawArr.map(r => eval(r)); // 使用reduce和concat拼接, return strArr.reduce( (acc, curr, index) => acc.concat(curr, valueArr[index] || ""), "" ); // 或者使用String.raw // return String.raw({ raw: strArr }, ...valueArr); } console.log(template(str));
上面使用eval
對插值進行了求值,實際上在平時使用中,eval
是不推薦的。並且用eval
去解析一些循環判斷和條件判斷也不是很方便。
因此接下來使用new Function()
去構建一個模板函數。在這以前,須要說明一下new Function
的用法。spa
new Function ([arg1[, arg2[, ...argN]],] functionBody)
前面傳入的是函數所須要的參數,最後是函數體,函數體是一個包括函數定義的 JavaScript 語句字符串。
其次,根據上面的插值實現,咱們可使用字符串拼接把插值計算以後和正常的字符串拼接起來。
對於簡單插值,使用{{}}
包裹,而語句使用{{~}}
區別。下面是一個簡單實現code
function render(tem, data) { let template = tem; template = template .replace(/[\r\n\t]/g, "") .replace(/\{\{~(.+?)\}\}/g, (_, p1) => { return '";' + p1 + ' out+="'; }) .replace(/\{\{(.+?)\}\}/g, (_, p1) => { return '"; out+=""+' + p1 + '+""; out+="'; }); template = 'var out=""; out += "' + template + '";return out;'; var _render = new Function(...Object.keys(data), template); return _render(...Object.keys(data).map(k => data[k])); } var template = "test array{{~for (var i in group.jobs) {}}{{group.jobs[i]}} {{~}}} test obj {{group.jobs[1]}} {{group.name}} leader是{{leader}}"; var data = { group: { name: "group1", jobs: ["job1", "job2"] }, leader: "張三" }; console.log(render(template, data));
在給模板注入數據時,可使用with(data){}
的方式,我不喜歡使用with
,因此把參數分解後傳入了。htm
(完)。模板引擎