詳解Javascript模板引擎mustache.js
本文總結(jié)它的使用方法和一些使用心得,內(nèi)容不算很高深,純粹是入門內(nèi)容,看看即可。不過(guò)要是你還沒(méi)有用過(guò)此類的javascript引擎庫(kù),那么本文還是值得你一讀的,相信在你了解完它強(qiáng)大的功能和簡(jiǎn)單用法之后,一定會(huì)迫不及待地將之用于你的工作當(dāng)中。
1. 從一個(gè)簡(jiǎn)單真實(shí)的需求講起
目前公司做了一個(gè)統(tǒng)一的開發(fā)平臺(tái),后臺(tái)封裝了MVC的接口和數(shù)據(jù)增刪改查的接口,前端我自己用bootstrap+手寫各類組件的方式弄了一套開發(fā)框架;集成了CAS,在CAS的基礎(chǔ)上,首先做了一套統(tǒng)一權(quán)限管理系統(tǒng),這個(gè)系統(tǒng)是我們開發(fā)平臺(tái)的第一個(gè)子系統(tǒng),用來(lái)管理配置所有子系統(tǒng)的菜單和授權(quán)以及管理整個(gè)公司的組織結(jié)構(gòu)和用戶,后來(lái)我們又陸陸續(xù)續(xù)地開發(fā)了業(yè)務(wù)系統(tǒng)A和業(yè)務(wù)系統(tǒng)B。由于這三個(gè)子系統(tǒng)對(duì)應(yīng)的是三個(gè)java工程,最終部署的時(shí)候,在tomcat里部署了三個(gè)應(yīng)用,現(xiàn)在有一個(gè)需求是:
- 1)在每個(gè)系統(tǒng)里登錄之后,點(diǎn)擊系統(tǒng)名稱,可以展開一個(gè)下拉菜單,顯示所有有權(quán)限的子系統(tǒng);
- 2)然后用戶點(diǎn)擊其它子系統(tǒng),就可以切換到所選中的系統(tǒng)去,到了其它系統(tǒng)之后,由于都做了這個(gè)下拉菜單,所以也可以再?gòu)脑撓到y(tǒng)切換回來(lái);
- 3)如果用戶只有一個(gè)系統(tǒng)的權(quán)限,則不顯示下拉菜單。
需求其實(shí)挺簡(jiǎn)單,原型大概是這個(gè)樣子:

功能實(shí)現(xiàn)方法是,在每個(gè)子系統(tǒng)登錄完成之后,調(diào)用獲取系統(tǒng)列表的接口,用js渲染一個(gè)下拉菜單出來(lái),該接口返回的格式為:
data: [
{
"sortOrder": 1,
"isCurrent": true,
"systemHttpUrl": "http://xxxx:8080/permission",
"systemName": "統(tǒng)一權(quán)限管理系統(tǒng)"
},
{
"sortOrder": 2,
"isCurrent": false,
"systemHttpUrl": "http://xxxx:8080/systemA",
"systemName": "業(yè)務(wù)系統(tǒng)A"
},
{
"sortOrder": 3,
"isCurrent": false,
"systemHttpUrl": "http://xxxx:8080/systemB",
"systemName": "業(yè)務(wù)系統(tǒng)B"
}
]
如果我們不采用模板引擎,那么傳統(tǒng)的方式去解析這個(gè)數(shù)據(jù)并把它轉(zhuǎn)變成html串的方法通常是:
function data2Html(data) {
data = data || [];
var html = ['<ul class="nav navbar-nav navbar-left nav-system">',
' <li class="dropdown">',
' <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown" title="切換系統(tǒng)">'],
l = data.length;
if(l < 2) {
l == 1 && html.push(data[0].systemName || '');
html.push('</a></li></ul>');
return html.join('');
}
var curSysAry = data.filter(function(s){ return s.isCurrent; });
html.push(curSysAry[0].systemName + ' <i class="fa fa-caret-down"></i></a><ul class="dropdown-menu">');
data.sort(function(a, b){ return a.sortOrder - b.sortOrder;});
for(var i = 0; i < l; i++) {
i && html.push('<li role="separator" class="divider"></li>');
html.push('<li><a href="' + data[i].systemHttpUrl + '" target="_self">' +
data[i].systemName + '</a></li>');
}
html.push('</ul></li></ul>');
return html.join('');
}
這種拼接字符串的方式有諸多弊端:
- 1)麻煩,尤其是拼接邏輯復(fù)雜,拼接的串很長(zhǎng)時(shí);
- 2)不易維護(hù),稍有不慎就會(huì)弄錯(cuò)標(biāo)簽的對(duì)應(yīng)關(guān)系;
- 3)結(jié)構(gòu)不清晰。
能夠簡(jiǎn)化這個(gè)場(chǎng)景的工具就是模板引擎,模板引擎的技術(shù)后臺(tái)最先有,如果你用過(guò)jsp,就一定知道jsp也就是一個(gè)模板,用來(lái)解析呈現(xiàn)數(shù)據(jù)用的,其它后臺(tái)模板引擎還有velocity和freemarker等等。前端的模板引擎也有很多,mustache.js算是比較流行的一個(gè),git上有8000多個(gè)贊,如果這個(gè)問(wèn)題我們用mustache.js來(lái)做,就可以變成這樣:
//通過(guò)一些根據(jù)屬性名稱對(duì)應(yīng)的標(biāo)記定義模板
var _template = [
'<ul class="nav navbar-nav navbar-left nav-system">',
' <li class="dropdown">',
' <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown" title="切換系統(tǒng)">',
' {{curSystemName}} {{#multiple}}<i class="fa fa-caret-down"></i>{{/multiple}}',
' </a>',
' {{#multiple}}<ul class="dropdown-menu">',
' {{#systems}}',
' {{^first}}<li role="separator" class="divider"></li>{{/first}}',
' <li>',
' <a href="{{{systemHttpUrl}}}" target="_self">{{systemName}}</a>',
' </li>',
' {{/systems}}',
' </ul>{{/multiple}}',
' </li>',
'</ul>'
].join('');
//初始化這個(gè)模板
Mustache.parse(_template);
function data2Html(data) {
data = data || [];
var curSysAry = data.filter(function(s){ return s.isCurrent; });
data.sort(function(a, b){ return a.sortOrder - b.sortOrder;});
data = data.map(function(s, i){s.first = i == 0; return s});
//模板渲染成字符串
return Mustache.render(_template, {
curSystemName: curSysAry.length ? curSysAry[0].systemName : '',
multiple: !!data.length,
systems: data
});
}
對(duì)比兩個(gè)代碼,會(huì)發(fā)現(xiàn)后面的代碼,相對(duì)于前面的有以下這些優(yōu)點(diǎn):
- 1)結(jié)構(gòu)清晰,所有待渲染的html都定義在一個(gè)位置,而且沒(méi)有任何拼接的現(xiàn)象;
- 2)邏輯清晰,那些在模板里的標(biāo)記,實(shí)際上與模板渲染時(shí)傳進(jìn)去的對(duì)象的屬性名稱都是對(duì)應(yīng)的;
- 3)易維護(hù),要增刪標(biāo)簽都只用調(diào)整模板對(duì)應(yīng)的數(shù)組就行了。
通過(guò)這個(gè)例子,應(yīng)該能對(duì)模板引擎有了一個(gè)大概的認(rèn)識(shí),這類工具在前端開發(fā)中越來(lái)越普遍,尤其是前后端分離的應(yīng)用中用的更多,已經(jīng)是這類應(yīng)用的基礎(chǔ)架構(gòu)的內(nèi)容了。mustache.js是一個(gè)非常簡(jiǎn)單易用的引擎實(shí)現(xiàn),接下來(lái)的內(nèi)容將會(huì)對(duì)這個(gè)工具常用的模板配置一一介紹并配合實(shí)用的例子說(shuō)明,希望能讓你更喜歡這個(gè)工具:)
2. mustache的用法
mustache的使用非常簡(jiǎn)單,先通過(guò)script標(biāo)簽引入它的js文件,然后按下面的步驟操作:
1)定義模板字符串
定義模板有2種方式,方式一就是在前面部分中看到的,直接用[...].join('')的方式在js代碼中定義,方式二直接把模板內(nèi)容用script定義在html中:
<script id="tpl" type="text/html">
Hello {{name}}!
</script>
然后在編譯模板之前,通過(guò)獲取tpl的innerHTML定義原始模板串:
var tpl = document.getElementById('tpl').innerHTML.trim();
具體要用哪種方式來(lái)定義模板,可以參考下面的建議:
如果這個(gè)模板要用于多個(gè)頁(yè)面,推薦把模板定義在js代碼中;如果這個(gè)模板只用于當(dāng)前頁(yè)面,推薦直接定義到script標(biāo)簽中,管理更方便。
2)預(yù)編譯模板
假設(shè)原始模板串已經(jīng)定義好,并用tpl變量來(lái)引用,就可以通過(guò)下面的代碼來(lái)預(yù)編譯模板:
Mustache.parse(tpl);
要注意的是,經(jīng)過(guò)預(yù)編譯之后的tpl已經(jīng)不再是原來(lái)的模板串了,連數(shù)據(jù)類型都變成數(shù)組類型了,這都是預(yù)編譯的結(jié)果。
3)渲染模板
渲染方式很簡(jiǎn)單:
var htmlAfterRendered = Mustache.render(tpl1, obj);
obj引用的是一個(gè)數(shù)據(jù)源對(duì)象,mustache會(huì)把模板中那些屬性標(biāo)簽,根據(jù)約定的規(guī)則,替換成對(duì)象的內(nèi)容。htmlAfterRendered就是替換之后的字符串,你可以用它完成你需要的DOM操作。
3. mustache的思想
mustache的核心是標(biāo)簽和logic-less。從前面的代碼中可以看到定義模板時(shí),使用了{(lán){name}}這樣的標(biāo)記,還有{{#systems}}{{/systems}},這就是mustache的標(biāo)簽,只不過(guò)它用{{}}替代了<>,以免跟html標(biāo)簽的<>混淆。logic-less,可以翻譯為輕邏輯,因?yàn)樵诙x模板的時(shí)候不會(huì)用到if-else,不會(huì)有循環(huán)式的編碼,一切都用標(biāo)簽來(lái)解決,它的標(biāo)簽非常簡(jiǎn)單,但是能應(yīng)付所有場(chǎng)景,閱讀完本文之后,你會(huì)驚訝地發(fā)現(xiàn),只要用以下幾個(gè)標(biāo)簽幾乎就能解決所有的問(wèn)題:
{{prop}}
{{{prop}}}
{{#prop}}{{/prop}}
{{^prop}}{{/prop}}
4. {{prop}}標(biāo)簽
這個(gè)標(biāo)簽是mustache模板里用的最多的,可以將數(shù)據(jù)源對(duì)象上prop屬性對(duì)應(yīng)的值,轉(zhuǎn)換成字符串進(jìn)行輸出,以下是同一個(gè)屬性,對(duì)應(yīng)不同類型的值,在經(jīng)過(guò)mustache渲染之后輸出結(jié)果的測(cè)試(前后那根短橫線的作用是為了讓這個(gè)標(biāo)簽的渲染結(jié)果看起來(lái)更清楚):
<script id="tpl1" type="text/html">
-{{prop}}-
</script>
<script>
var tpl1 = document.getElementById('tpl1').innerHTML.trim();
Mustache.parse(tpl1);
//測(cè)試falsy值
console.log(Mustache.render(tpl1, {prop: ''}));//--
console.log(Mustache.render(tpl1, {prop: 0}));//-0-
console.log(Mustache.render(tpl1, {prop: null}));//--
console.log(Mustache.render(tpl1, {prop: undefined}));//--
console.log(Mustache.render(tpl1, {prop: false}));//-false-
console.log(Mustache.render(tpl1, {prop: NaN}));//-NaN-
//測(cè)試簡(jiǎn)單對(duì)象
console.log(Mustache.render(tpl1, {prop: {name: 'jason'}}));//-[object Object]-
//測(cè)試數(shù)組
console.log(Mustache.render(tpl1, {prop: [{name: 'jason'}, {name: 'frank'}]}));//-[object Object],[object Object]-
//測(cè)試日期對(duì)象
console.log(Mustache.render(tpl1, {prop: new Date()}));//-Mon Jan 18 2016 15:38:46 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間)-
//測(cè)試自定義toString的簡(jiǎn)單對(duì)象
var obj1 = {name: 'jason'};
obj1.toString = function () {
return this.name;
};
console.log(Mustache.render(tpl1, {prop: obj1}));//-jason-
//測(cè)試boolean number string
console.log(Mustache.render(tpl1, {prop: true}));//-true-
console.log(Mustache.render(tpl1, {prop: 1.2}));//-1.2-
console.log(Mustache.render(tpl1, {prop: 'yes'}));//-yes-
//測(cè)試function
console.log(Mustache.render(tpl1, {
prop: function () {
}
}));//--
console.log(Mustache.render(tpl1, {
prop: function () {
return 'it\'s a fun'
}
}));//-it's a fun-
console.log(Mustache.render(tpl1, {
prop: function () {
return false;
}
}));//-false-
console.log(Mustache.render(tpl1, {
prop: function(){
return function (text, render) {
return "<b>" + render(text) + "</b>"
};
}
}));
//-function (text, render) {
// return "<b>" + render(text) + "</b>"
//}-
</script>
mustache渲染{{prop}}標(biāo)簽的邏輯是:
- 1)如果prop引用的值是null或undefined,則渲染成空串;
- 2)如果prop引用的是一個(gè)函數(shù),則在渲染時(shí)自動(dòng)執(zhí)行這個(gè)函數(shù),并把這個(gè)函數(shù)的返回值作為渲染結(jié)果,假如這個(gè)返回值為null或者undefined,那么渲染結(jié)果仍然為空串,否則把返回值轉(zhuǎn)成字符串作為渲染結(jié)果(注意最后一個(gè)用例,直接把函數(shù)代碼渲染出來(lái)了);
- 3)其它場(chǎng)景,直接把prop引用的值轉(zhuǎn)成字符串作為渲染結(jié)果。
由于默認(rèn)情況下,mustache在渲染prop時(shí),都是對(duì)prop的原始值進(jìn)行url編碼或者h(yuǎn)tml編碼之后再輸出的,所以有一個(gè)用例的渲染結(jié)果,把英文的單引號(hào),轉(zhuǎn)成了html實(shí)體符:
console.log(Mustache.render(tpl1, {
prop: function () {
return 'it\'s a fun'
}
}));//-it's a fun-
如果要阻止這種編碼行為,只要把標(biāo)簽形式改成{{{prop}}}就可以了:
<script id="tpl1" type="text/html">
-{{{prop}}}-
</script>
console.log(Mustache.render(tpl1, {
prop: function () {
return 'it\'s a fun'
}
}));//-it's a fun-
5. {{#prop}}{{/prop}}標(biāo)簽
這對(duì)標(biāo)簽的作用非常強(qiáng)大,可以同時(shí)完成if-else和for-each以及動(dòng)態(tài)渲染的模板功能。在這對(duì)標(biāo)簽之間,可以定義其它模板內(nèi)容,嵌套所有標(biāo)簽。接下來(lái)看看mustache如何利用這個(gè)對(duì)標(biāo)簽完成這三個(gè)模板功能。
1) if-else渲染
只有prop屬性在數(shù)據(jù)源對(duì)象上存在,并且不為falsy值(javascript 6個(gè)falsy值:null,undefined,NaN,0,false,空字符串),并且不為空數(shù)組的情況下,標(biāo)簽之間的內(nèi)容才會(huì)被渲染,否則都不會(huì)被渲染:
<script id="tpl2" type="text/html">
-{{#prop}}content{{/prop}}-
</script>
<script>
var tpl2 = document.getElementById('tpl2').innerHTML.trim();
Mustache.parse(tpl2);
//測(cè)試falsy值
console.log(Mustache.render(tpl2, {prop: ''}));//--
console.log(Mustache.render(tpl2, {prop: 0}));//--
console.log(Mustache.render(tpl2, {prop: null}));//--
console.log(Mustache.render(tpl2, {prop: undefined}));//--
console.log(Mustache.render(tpl2, {prop: false}));//--
console.log(Mustache.render(tpl2, {prop: NaN}));//--
//測(cè)試空數(shù)組
console.log(Mustache.render(tpl2, {prop: []}));//--
//測(cè)試不存在的屬性
console.log(Mustache.render(tpl2, {prop2:
true
}));//--
//測(cè)試function
console.log(Mustache.render(tpl2, {
prop: function () {
}
}));//--
console.log(Mustache.render(tpl2, {
prop: function () {
return false;
}
}));//--
console.log(Mustache.render(tpl2, {
prop: function() {
return [];
}
}));//--
//測(cè)試簡(jiǎn)單對(duì)象
console.log(Mustache.render(tpl2, {prop: {name: 'jason'}}));//-content-
//測(cè)試日期對(duì)象
console.log(Mustache.render(tpl2, {prop: new Date()}));//-content-
//測(cè)試boolean number string
console.log(Mustache.render(tpl2, {prop: true}));//-content-
console.log(Mustache.render(tpl2, {prop: 1.2}));//-content-
console.log(Mustache.render(tpl2, {prop: 'yes'}));//-content-
//測(cè)試返回非falsy,非空數(shù)組的function
console.log(Mustache.render(tpl2, {
prop: function () {
return 'it\'s a fun'
}
}));//-content-
</script>
以上用例中特殊點(diǎn)的就是prop屬性引用的是一個(gè)函數(shù)的時(shí)候,{{#prop}}會(huì)自動(dòng)調(diào)用這個(gè)函數(shù),并把函數(shù)的返回值作為if-else渲染邏輯的判斷依據(jù),也就是說(shuō)如果這個(gè)函數(shù)返回的是falsy值或者是空數(shù)組的時(shí)候,那么這對(duì)標(biāo)簽之間的內(nèi)容還是不會(huì)顯示。
2)for-each渲染
當(dāng)prop屬性所引用的是一個(gè)非空數(shù)組時(shí),這對(duì)標(biāo)簽之間的內(nèi)容將會(huì)根據(jù)數(shù)組大小進(jìn)行迭代,并且當(dāng)數(shù)組元素為對(duì)象時(shí),還會(huì)把該對(duì)象作為每一次迭代的上下文,以便迭代時(shí)的標(biāo)簽可以直接引用數(shù)組元素上的屬性:
<script id="tpl2" type="text/html">
-{{#prop}}{{name}},{{/prop}}-
</script>
<script>
var tpl2 = document.getElementById('tpl2').innerHTML.trim();
Mustache.parse(tpl2);
console.log(Mustache.render(tpl2, {prop: [{name: 'jason'}, {name: 'frank'}]}));//-jason,frank,-
</script>
從這個(gè)測(cè)試結(jié)果中可以看到,{{#prop}}{{/prop}}之間的模板內(nèi)容根據(jù)prop所引用的數(shù)組迭代了兩次,并且在這對(duì)標(biāo)簽內(nèi)部直接通過(guò){{name}}標(biāo)簽,輸出了數(shù)組元素對(duì)象上的name屬性對(duì)應(yīng)的值。
如果prop屬性所引用的是一個(gè)函數(shù),但是這個(gè)函數(shù)返回值是一個(gè)數(shù)組類型,那么仍然會(huì)進(jìn)行for-each渲染:
<script id="tpl2" type="text/html">
-{{#prop}}{{name}},{{/prop}}-
</script>
<script>
var tpl2 = document.getElementById('tpl2').innerHTML.trim();
Mustache.parse(tpl2);
console.log(Mustache.render(tpl2, {
prop: function(){
return [{name: 'jason'}, {name: 'frank'}];
}
}));//-jason,frank,-
</script>
3) 動(dòng)態(tài)渲染
當(dāng)prop屬性所引用的是一個(gè)函數(shù),并且這個(gè)函數(shù)的返回值還是一個(gè)函數(shù)的話,mustache會(huì)再次調(diào)用這個(gè)返回的函數(shù),并給它傳遞2個(gè)參數(shù):text表示原來(lái)的模板內(nèi)容,render表示mustache內(nèi)部的執(zhí)行渲染的對(duì)象,以便在這個(gè)函數(shù)內(nèi)部可以通過(guò)這render對(duì)象,結(jié)合原來(lái)的模板內(nèi)容,自定義渲染的邏輯,并把函數(shù)的返回值作為渲染結(jié)果(這個(gè)返回值渲染的邏輯跟{{prop}}標(biāo)簽完全一樣):
<script id="tpl2" type="text/html">
-{{#prop}}content{{/prop}}-
</script>
<script>
var tpl2 = document.getElementById('tpl2').innerHTML.trim();
Mustache.parse(tpl2);
console.log(Mustache.render(tpl2, {
prop: function(){
return function (text, render) {
return "<b>" + render(text) + "</b>"
};
}
}));//-<b>content</b>-
</script>
6. {{^prop}}{{/prop}}標(biāo)簽
這對(duì)標(biāo)簽,與{{#prop}}{{/prop}}的if-else渲染執(zhí)行相反邏輯,即只有在prop屬性不存在,或者引用的是一個(gè)falsy值,或者是一個(gè)空數(shù)組的時(shí)候才會(huì)顯示標(biāo)簽之間的內(nèi)容,否則不會(huì)顯示:
<script id="tpl2" type="text/html">
-{{^prop}}content{{/prop}}-
</script>
<script>
var tpl2 = document.getElementById('tpl2').innerHTML.trim();
Mustache.parse(tpl2);
//測(cè)試falsy值
console.log(Mustache.render(tpl2, {prop: ''}));//-content-
console.log(Mustache.render(tpl2, {prop: 0}));//-content-
console.log(Mustache.render(tpl2, {prop: null}));//-content-
console.log(Mustache.render(tpl2, {prop: undefined}));//-content-
console.log(Mustache.render(tpl2, {prop: false}));//-content-
console.log(Mustache.render(tpl2, {prop: NaN}));//-content-
// 測(cè)試空數(shù)組
console.log(Mustache.render(tpl2, {prop: []}));//-content-
// 測(cè)試不存在的屬性
console.log(Mustache.render(tpl2, {prop2: true}));//-content-
//測(cè)試function
console.log(Mustache.render(tpl2, {
prop: function () {
}
}));//-content-
console.log(Mustache.render(tpl2, {
prop: function () {
return false;
}
}));//-content-
console.log(Mustache.render(tpl2, {
prop: function () {
return [];
}
}));//-content-
//測(cè)試簡(jiǎn)單對(duì)象
console.log(Mustache.render(tpl2, {prop: {name: 'jason'}}));//--
//測(cè)試日期對(duì)象
console.log(Mustache.render(tpl2, {prop: new Date()}));//--
// 測(cè)試非空數(shù)組
console.log(Mustache.render(tpl2, {prop: [{name: 'jason'},{name: 'tom'}]}));//--
//測(cè)試boolean number string
console.log(Mustache.render(tpl2, {prop: true}));//--
console.log(Mustache.render(tpl2, {prop: 1.2}));//--
console.log(Mustache.render(tpl2, {prop: 'yes'}));//--
//測(cè)試返回非falsy,非空數(shù)組的function
console.log(Mustache.render(tpl2, {
prop: function () {
return 'it\'s a fun'
}
}));//--
//測(cè)試返回function的function
console.log(Mustache.render(tpl2, {
prop: function () {
return function(text,render){
return '<b>' + render(text) +'</b>'
}
}
}));//--
</script>
7. 渲染上下文
mustache有一個(gè)渲染上下文棧的概念,在模板渲染的開始的時(shí)候,把數(shù)據(jù)源對(duì)象作為當(dāng)前的渲染上下文 ,并壓入上下文棧。在遇到{{#prop}}標(biāo)簽的時(shí)候,如果prop引用的是一個(gè)對(duì)象或者是一個(gè)非空的對(duì)象數(shù)組,或者prop引用的是一個(gè)函數(shù),并且這個(gè)函數(shù)返回的是一個(gè)對(duì)象或者是一個(gè)非空的對(duì)象數(shù)組,就會(huì)把這個(gè)對(duì)象或者數(shù)組的元素作為當(dāng)前渲染上下文,并壓入上下文棧,當(dāng)這個(gè)標(biāo)簽渲染完畢的時(shí)候,才會(huì)把該上下文彈出,恢復(fù)上一層標(biāo)簽所使用的上下文。由于{{#prop}}標(biāo)簽可以多層嵌套,所以在有的模板渲染的時(shí)候,會(huì)有多層上下文存在。mustache在解析標(biāo)簽時(shí),根據(jù)標(biāo)簽名稱查找當(dāng)前上下文對(duì)象是否存在該屬性,如果不存在就會(huì)到上層上下文對(duì)象中查找,只要在某一層找到,就會(huì)用該層上下文對(duì)象的值來(lái)渲染。
<script id="tpl2" type="text/html">
-{{#person}}{{#student}}{{#address}}address: {{home}},age: {{age}}{{/address}}{{/student}}{{/person}}-
</script>
<script>
var tpl2 = document.getElementById('tpl2').innerHTML.trim();
var obj2 = {
age: 20,
person: {
student: {
address: {
home: 'xxxxx'
}
}
}
};
console.log(Mustache.render(tpl2, obj2));//-address: xxxxx,age: 20-
</script>
上面這個(gè)例子中,在渲染{{#address}}{{/address}}時(shí),上下文對(duì)象已經(jīng)變成了obj2.person.student.address所引用的對(duì)象,所以{{home}}渲染時(shí)用到的就是obj2.person.student.address.home屬性,而{{age}}渲染時(shí),由于obj2.person.student.address不存在age屬性,所以就會(huì)到上層上下文中查找,一直到obj2對(duì)象才找到,于是就把obj2.age當(dāng)成了渲染結(jié)果。
還有一種方法,不用通過(guò){{#prop}}創(chuàng)建新的上下文,也可以做到遞歸渲染屬性的屬性:
<script id="tpl2" type="text/html">
-address: {{person.student.address.home}},age: {{age}}-
</script>
<script>
var tpl2 = document.getElementById('tpl2').innerHTML.trim();
var obj2 = {
age: 20,
person: {
student: {
address: {
home: 'xxxxx'
}
}
}
};
console.log(Mustache.render(tpl2, obj2));//-address: xxxxx,age: 20-
</script>
這種方法其實(shí)很好理解,只要知道當(dāng)前的上下文對(duì)象,再根據(jù)屬性操作串person.student.address.home,當(dāng)然就能找到需要的值了。
本文介紹了一個(gè)非常好用的前端模板引擎,涵蓋的內(nèi)容包含了在日常工作肯定會(huì)用到的知識(shí)點(diǎn),希望大家喜歡。
相關(guān)文章
基于javascript的JSON格式頁(yè)面展示美化方法
本文介紹的方法基于javascript,css和myFunction結(jié)合可實(shí)現(xiàn)頁(yè)面展示轉(zhuǎn)換,下面有個(gè)示例,大家可以參考下2014-07-07
關(guān)于JavaScript中parseInt()的一個(gè)怪異行為解決
parseInt()是內(nèi)置的?JS?函數(shù),用于解析數(shù)字字符串中的整數(shù),下面這篇文章主要給大家介紹了關(guān)于JavaScript中parseInt()的一個(gè)怪異行為解決,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
微信小程序基于canvas漸變實(shí)現(xiàn)的彩虹效果示例
這篇文章主要介紹了微信小程序基于canvas漸變實(shí)現(xiàn)的彩虹效果,結(jié)合實(shí)例形式分析了微信小程序線性漸變及圓形漸變的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-05-05
layui表格 列自動(dòng)適應(yīng)大小失效的解決方法
今天小編就為大家分享一篇layui表格 列自動(dòng)適應(yīng)大小失效的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09
JavaScript實(shí)現(xiàn)簡(jiǎn)單的四則運(yùn)算計(jì)算器完整實(shí)例
這篇文章主要介紹了JavaScript實(shí)現(xiàn)簡(jiǎn)單的四則運(yùn)算計(jì)算器,結(jié)合完整實(shí)例形式分析了javascript基于表單相應(yīng)實(shí)現(xiàn)加減乘除數(shù)學(xué)運(yùn)算的操作技巧,需要的朋友可以參考下2017-04-04
利用JavaScript實(shí)現(xiàn)簡(jiǎn)單全選和全不選的思路和方法
最近開始練習(xí)js的基本操作,常常因?yàn)橐恍┬∈韬鰧?dǎo)致頁(yè)面效果無(wú)法實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于利用JavaScript實(shí)現(xiàn)簡(jiǎn)單全選和不全選的思路和方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07

