什麼是前端模板引擎?
通俗地說,前端模板引擎就是你須要根據不一樣數據,重複生成結構相同的html的時候,模板能夠大大節省你的代碼量,以及提升可維護性。html
爲何要用前端模板引擎?
- 節省代碼量。
- 減小拼接HTML的麻煩。
- 可維護性好,後期修改起來比較方便。
- 開發效率高(程序邏輯組織更好,調試方便)。
- 看起來舒服(不容易寫錯)。
- 有利於先後端分離。
實現前端模板引擎。
在編寫前端模板引擎代碼以前,咱們應該想好如何來調用它,即這個模板引擎的接口應該是什麼樣的。咱們但願這樣調用它:前端
// 建立一個模板引擎:
var tpl = new Template('<p>Today: { date }</p>\n<a href="/{ user.id|safe }">{ user.company }</a>');
// 渲染獲得HTML片斷:
var model = {
date: 20150316,
user: {
id: 'A-000&001',
company: 'AT&T'
}
};
var html = tpl.render(model);
console.log(html);
// <p>Today: 20150316</p>
// <a href="/A-000&001">AT&T</a>
這個思路仍是很明確的,一個模板引擎就是把一個字符串中的變量使用 medel 的變量替換掉,就完成了。 規則也不難,就是對於通常的標籤正常表示,對於變量,咱們使用{}來包裹。 git
咱們選用{ model.prop }
來實現咱們本身的變量替換,基本思想是用一個正則表達式來匹配{ xxx.xxx }
:github
var re = /\{\s*([a-zA-Z\.\_0-9()]+)\s*\}/m
var match = re.exec('a { template } string');
編寫一個簡單的JavaScript模板引擎
廖雪峯 / 編程 / 2015-3-16 21:33 / 閱讀: 607正則表達式
隨着Nodejs的流行,JavaScript在前端和後端都開始流行起來。有許多成熟的JavaScript模板引擎,例如Swig,既能夠用在後端,又能夠用在前端。編程
不過不少時候,前端模板僅僅須要簡單地建立一個HTML片斷,用Swig這種全功能模板有點大材小用。咱們來嘗試本身編寫一個簡單的前端模板引擎,實際上並不複雜。後端
在編寫前端模板引擎代碼以前,咱們應該想好如何來調用它,即這個模板引擎的接口應該是什麼樣的。咱們但願這樣調用它:數組
// 建立一個模板引擎:
var tpl = new Template('<p>Today: { date }</p>\n<a href="/{ user.id|safe }">{ user.company }</a>'); // 渲染獲得HTML片斷: var model = { date: 20150316, user: { id: 'A-000&001', company: 'AT&T' } }; var html = tpl.render(model); console.log(html); // <p>Today: 20150316</p> // <a href="/A-000&001">AT&T</a>
所以,一個模板引擎就是把一個字符串中的變量用model
的變量替換掉,就完成了。app
像Swig這種類Jinja2的模板引擎,它能夠替換{{ model.prop }}
這樣的變量。前後端分離
咱們選用{ model.prop }
來實現咱們本身的變量替換,基本思想是用一個正則表達式來匹配{ xxx.xxx }
:
var re = /\{\s*([a-zA-Z\.\_0-9()]+)\s*\}/m
var match = re.exec('a { template } string');
(其中 \s 爲空格, () 用來捕獲(使用execf方法),()中間的就是插入的變量)
若是正則匹配成功,則match
不爲空,match[0]
是匹配到的字符串{ template }
,match[1]
是捕獲的變量template
,match.index
是匹配的索引。
經過這種方式,咱們就能夠不斷的匹配到變量,而後用model的內容來替換了,最後獲得HTML,這就是模板引擎的原理。
可是,想要分析 user.addr.zipcode 而後去model中查找並不容易,並且模板應該能夠預編譯,這樣,後續的渲染速度就會很快。
JavaScript容許用new Function('source')
來經過字符串建立一個函數,這個函數和咱們用function ()
定義的函數是如出一轍的,所以,一個模板引擎的編譯過程就是建立一個函數,而後調用該函數就實現了模板渲染。
須要編譯的函數代碼應該像這樣:
function () {
var r = [];
r.push('<p>Today: ');
r.push(this.date);
r.push('</p>\n<a href="/');
r.push(this.user.id);
r.push('">');
r.push(this.user.company);
r.push('</a>');
return r.join('');
}
注意到變量名從variable.prop
變成了this.variable.prop
,是由於調用該函數時咱們會把model
綁定到this
變量上.
所以,模板引擎的代碼以下:
function Template(tpl) {
var
fn,
match,
code = ['var r=[];'],
re = /\{\s*([a-zA-Z\.\_0-9()]+)\s*\}/m,
addLine = function (text) {
code.push('r.push(\'' + text.replace(/\'/g, '\\\'').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '\');');
};
while (match = re.exec(tpl)) {
if (match.index > 0) {
addLine(tpl.slice(0, match.index));
}
code.push('r.push(this.' + match[1] + ');');
tpl = tpl.substring(match.index + match[0].length);
}
addLine(tpl);
code.push('return r.join(\'\');');
// 建立函數:
fn = new Function(code.join('\n'));
// 用render()調用函數並綁定this參數:
this.render = function (model) {
return fn.apply(model);
};
}
如今,這個簡單的模板引擎已經能夠工做了。可是它還有幾個小問題須要解決,一是默認的變量在替換時應該作HTML轉義,二是若是某些不須要轉義的變量,能夠用{ user.id|safe }
這樣的表達式表示user.id
無需轉義。
通過HTML轉義和{ variable|safe }
處理的最終代碼以下
function Template(tpl) {
var
fn,
match,
code = ['var r=[];\nvar _html = function (str) { return str.replace(/&/g, \'&\').replace(/"/g, \'"\').replace(/\'/g, \''\').replace(/</g, \'<\').replace(/>/g, \'>\'); };'],
re = /\{\s*([a-zA-Z\.\_0-9()]+)(\s*\|\s*safe)?\s*\}/m,
addLine = function (text) {
code.push('r.push(\'' + text.replace(/\'/g, '\\\'').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '\');');
};
while (match = re.exec(tpl)) {
if (match.index > 0) {
addLine(tpl.slice(0, match.index));
}
if (match[2]) {
code.push('r.push(String(this.' + match[1] + '));');
}
else {
code.push('r.push(_html(String(this.' + match[1] + ')));');
}
tpl = tpl.substring(match.index + match[0].length);
}
addLine(tpl);
code.push('return r.join(\'\');');
fn = new Function(code.join('\n'));
this.render = function (model) {
return fn.apply(model);
};
}
如今就能夠用咱們預設的代碼來使用這個模板引擎了。不過,把模板寫在字符串中也不是一個好辦法。最佳解決方案是利用<script>
標籤,把模板寫在裏面,注意必定要加上type="text/plain"
:
<script id="tpl" type="text/plain">
<p>Today: { date }</p>
<a href="/{ user.id|safe }">{ user.company }</a>
</script>
而後,用jQuery來得到模板內容並渲染:
var tpl = new Template($('#tpl').html());
var s = tpl.render({
date: 20150101,
user: {
id: 'A-000&001',
company: 'AT&T'
}
});
$('#other').html(s);
這樣,咱們就用不到30行代碼實現了一個簡單的JavaScript模板引擎。
最後,咱們再總結一下思路: 首先,使用模板引擎的格式來寫這麼一個模板,而後通過一個函數處理,過程就是經過while循環,而後捕獲到對應的變量的規則,將變量前的內容push到數組中,而後再使用數據進行替換,而後push到數組中,接着經過字符串方法把前面的字符串截取,留下後面的字符串繼續匹配,最後,咱們就能夠經過join()方法來實現html的生成了,經過appendChild()就能夠順利插入數組了。