亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Javascript模板引擎mustache.js

 更新時間:2016年01月20日 10:47:25   投稿:lijiao  
這篇文章主要為大家介紹了Javascript模板引擎mustache.js,mustache.js是一個簡單強大的Javascript模板引擎,使用它可以簡化在js代碼中的html編寫,壓縮后只有9KB,非常值得在項目中使用,需要的朋友可以參考下

本文總結(jié)它的使用方法和一些使用心得,內(nèi)容不算很高深,純粹是入門內(nèi)容,看看即可。不過要是你還沒有用過此類的javascript引擎庫,那么本文還是值得你一讀的,相信在你了解完它強大的功能和簡單用法之后,一定會迫不及待地將之用于你的工作當中。

1. 從一個簡單真實的需求講起
目前公司做了一個統(tǒng)一的開發(fā)平臺,后臺封裝了MVC的接口和數(shù)據(jù)增刪改查的接口,前端我自己用bootstrap+手寫各類組件的方式弄了一套開發(fā)框架;集成了CAS,在CAS的基礎上,首先做了一套統(tǒng)一權限管理系統(tǒng),這個系統(tǒng)是我們開發(fā)平臺的第一個子系統(tǒng),用來管理配置所有子系統(tǒng)的菜單和授權以及管理整個公司的組織結(jié)構(gòu)和用戶,后來我們又陸陸續(xù)續(xù)地開發(fā)了業(yè)務系統(tǒng)A和業(yè)務系統(tǒng)B。由于這三個子系統(tǒng)對應的是三個java工程,最終部署的時候,在tomcat里部署了三個應用,現(xiàn)在有一個需求是:

  • 1)在每個系統(tǒng)里登錄之后,點擊系統(tǒng)名稱,可以展開一個下拉菜單,顯示所有有權限的子系統(tǒng);
  • 2)然后用戶點擊其它子系統(tǒng),就可以切換到所選中的系統(tǒng)去,到了其它系統(tǒng)之后,由于都做了這個下拉菜單,所以也可以再從該系統(tǒng)切換回來;
  • 3)如果用戶只有一個系統(tǒng)的權限,則不顯示下拉菜單。

需求其實挺簡單,原型大概是這個樣子:

功能實現(xiàn)方法是,在每個子系統(tǒng)登錄完成之后,調(diào)用獲取系統(tǒng)列表的接口,用js渲染一個下拉菜單出來,該接口返回的格式為:

data: [
 {
  "sortOrder": 1,
  "isCurrent": true, 
"systemHttpUrl": "http://xxxx:8080/permission",
  "systemName": "統(tǒng)一權限管理系統(tǒng)"
 },
 {
  "sortOrder": 2,
  "isCurrent": false, 
  "systemHttpUrl": "http://xxxx:8080/systemA",
  "systemName": "業(yè)務系統(tǒng)A"
 },
 {
  "sortOrder": 3,
  "isCurrent": false, 
  "systemHttpUrl": "http://xxxx:8080/systemB",
  "systemName": "業(yè)務系統(tǒng)B"
 }
]

如果我們不采用模板引擎,那么傳統(tǒng)的方式去解析這個數(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)麻煩,尤其是拼接邏輯復雜,拼接的串很長時;
  • 2)不易維護,稍有不慎就會弄錯標簽的對應關系;
  • 3)結(jié)構(gòu)不清晰。

能夠簡化這個場景的工具就是模板引擎,模板引擎的技術后臺最先有,如果你用過jsp,就一定知道jsp也就是一個模板,用來解析呈現(xiàn)數(shù)據(jù)用的,其它后臺模板引擎還有velocity和freemarker等等。前端的模板引擎也有很多,mustache.js算是比較流行的一個,git上有8000多個贊,如果這個問題我們用mustache.js來做,就可以變成這樣:

//通過一些根據(jù)屬性名稱對應的標記定義模板
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('');

//初始化這個模板
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
 });
}

對比兩個代碼,會發(fā)現(xiàn)后面的代碼,相對于前面的有以下這些優(yōu)點:

  • 1)結(jié)構(gòu)清晰,所有待渲染的html都定義在一個位置,而且沒有任何拼接的現(xiàn)象;
  • 2)邏輯清晰,那些在模板里的標記,實際上與模板渲染時傳進去的對象的屬性名稱都是對應的;
  • 3)易維護,要增刪標簽都只用調(diào)整模板對應的數(shù)組就行了。

通過這個例子,應該能對模板引擎有了一個大概的認識,這類工具在前端開發(fā)中越來越普遍,尤其是前后端分離的應用中用的更多,已經(jīng)是這類應用的基礎架構(gòu)的內(nèi)容了。mustache.js是一個非常簡單易用的引擎實現(xiàn),接下來的內(nèi)容將會對這個工具常用的模板配置一一介紹并配合實用的例子說明,希望能讓你更喜歡這個工具:)

2. mustache的用法
mustache的使用非常簡單,先通過script標簽引入它的js文件,然后按下面的步驟操作:
1)定義模板字符串
定義模板有2種方式,方式一就是在前面部分中看到的,直接用[...].join('')的方式在js代碼中定義,方式二直接把模板內(nèi)容用script定義在html中:

<script id="tpl" type="text/html">
 Hello {{name}}!
</script>

然后在編譯模板之前,通過獲取tpl的innerHTML定義原始模板串:

var tpl = document.getElementById('tpl').innerHTML.trim();

具體要用哪種方式來定義模板,可以參考下面的建議:
如果這個模板要用于多個頁面,推薦把模板定義在js代碼中;如果這個模板只用于當前頁面,推薦直接定義到script標簽中,管理更方便。
2)預編譯模板
假設原始模板串已經(jīng)定義好,并用tpl變量來引用,就可以通過下面的代碼來預編譯模板:

Mustache.parse(tpl);

要注意的是,經(jīng)過預編譯之后的tpl已經(jīng)不再是原來的模板串了,連數(shù)據(jù)類型都變成數(shù)組類型了,這都是預編譯的結(jié)果。
3)渲染模板
渲染方式很簡單:

var htmlAfterRendered = Mustache.render(tpl1, obj);

obj引用的是一個數(shù)據(jù)源對象,mustache會把模板中那些屬性標簽,根據(jù)約定的規(guī)則,替換成對象的內(nèi)容。htmlAfterRendered就是替換之后的字符串,你可以用它完成你需要的DOM操作。

3. mustache的思想
mustache的核心是標簽和logic-less。從前面的代碼中可以看到定義模板時,使用了{{name}}這樣的標記,還有{{#systems}}{{/systems}},這就是mustache的標簽,只不過它用{{}}替代了<>,以免跟html標簽的<>混淆。logic-less,可以翻譯為輕邏輯,因為在定義模板的時候不會用到if-else,不會有循環(huán)式的編碼,一切都用標簽來解決,它的標簽非常簡單,但是能應付所有場景,閱讀完本文之后,你會驚訝地發(fā)現(xiàn),只要用以下幾個標簽幾乎就能解決所有的問題:
{{prop}}
{{{prop}}}
{{#prop}}{{/prop}}
{{^prop}}{{/prop}}

4. {{prop}}標簽
這個標簽是mustache模板里用的最多的,可以將數(shù)據(jù)源對象上prop屬性對應的值,轉(zhuǎn)換成字符串進行輸出,以下是同一個屬性,對應不同類型的值,在經(jīng)過mustache渲染之后輸出結(jié)果的測試(前后那根短橫線的作用是為了讓這個標簽的渲染結(jié)果看起來更清楚):

<script id="tpl1" type="text/html">
 -{{prop}}-
</script>
<script>
 var tpl1 = document.getElementById('tpl1').innerHTML.trim();
 Mustache.parse(tpl1);
 //測試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-

 //測試簡單對象
 console.log(Mustache.render(tpl1, {prop: {name: 'jason'}}));//-[object Object]-
 //測試數(shù)組
 console.log(Mustache.render(tpl1, {prop: [{name: 'jason'}, {name: 'frank'}]}));//-[object Object],[object Object]-
 //測試日期對象
 console.log(Mustache.render(tpl1, {prop: new Date()}));//-Mon Jan 18 2016 15:38:46 GMT+0800 (中國標準時間)-
 //測試自定義toString的簡單對象
 var obj1 = {name: 'jason'};
 obj1.toString = function () {
  return this.name;
 };
 console.log(Mustache.render(tpl1, {prop: obj1}));//-jason-

 //測試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-

 //測試function
 console.log(Mustache.render(tpl1, {
  prop: function () {
  }
 }));//--
 console.log(Mustache.render(tpl1, {
  prop: function () {
   return 'it\'s a fun'
  }
 }));//-it&#39;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 &quot;&lt;b&gt;&quot; + render(text) + &quot;&lt;&#x2F;b&gt;&quot;
 //}-

</script>

mustache渲染{{prop}}標簽的邏輯是:

  • 1)如果prop引用的值是null或undefined,則渲染成空串;
  • 2)如果prop引用的是一個函數(shù),則在渲染時自動執(zhí)行這個函數(shù),并把這個函數(shù)的返回值作為渲染結(jié)果,假如這個返回值為null或者undefined,那么渲染結(jié)果仍然為空串,否則把返回值轉(zhuǎn)成字符串作為渲染結(jié)果(注意最后一個用例,直接把函數(shù)代碼渲染出來了);
  • 3)其它場景,直接把prop引用的值轉(zhuǎn)成字符串作為渲染結(jié)果。

由于默認情況下,mustache在渲染prop時,都是對prop的原始值進行url編碼或者html編碼之后再輸出的,所以有一個用例的渲染結(jié)果,把英文的單引號,轉(zhuǎn)成了html實體符:

console.log(Mustache.render(tpl1, {
  prop: function () {
   return 'it\'s a fun'
  }
}));//-it&#39;s a fun-

如果要阻止這種編碼行為,只要把標簽形式改成{{{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}}標簽
這對標簽的作用非常強大,可以同時完成if-else和for-each以及動態(tài)渲染的模板功能。在這對標簽之間,可以定義其它模板內(nèi)容,嵌套所有標簽。接下來看看mustache如何利用這個對標簽完成這三個模板功能。

1) if-else渲染

只有prop屬性在數(shù)據(jù)源對象上存在,并且不為falsy值(javascript 6個falsy值:null,undefined,NaN,0,false,空字符串),并且不為空數(shù)組的情況下,標簽之間的內(nèi)容才會被渲染,否則都不會被渲染:

<script id="tpl2" type="text/html">
 -{{#prop}}content{{/prop}}-
</script>
<script>
 var tpl2 = document.getElementById('tpl2').innerHTML.trim();
 Mustache.parse(tpl2);
 //測試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}));//--
 //測試空數(shù)組
 console.log(Mustache.render(tpl2, {prop: []}));//--
 //測試不存在的屬性
 console.log(Mustache.render(tpl2, {prop2: 
true
}));//--
 //測試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 [];
  }
 }));//--
 
 //測試簡單對象
 console.log(Mustache.render(tpl2, {prop: {name: 'jason'}}));//-content-
 //測試日期對象
 console.log(Mustache.render(tpl2, {prop: new Date()}));//-content-
 //測試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-
 //測試返回非falsy,非空數(shù)組的function
 console.log(Mustache.render(tpl2, {
  prop: function () {
   return 'it\'s a fun'
  }
 }));//-content-
</script>

以上用例中特殊點的就是prop屬性引用的是一個函數(shù)的時候,{{#prop}}會自動調(diào)用這個函數(shù),并把函數(shù)的返回值作為if-else渲染邏輯的判斷依據(jù),也就是說如果這個函數(shù)返回的是falsy值或者是空數(shù)組的時候,那么這對標簽之間的內(nèi)容還是不會顯示。

2)for-each渲染

當prop屬性所引用的是一個非空數(shù)組時,這對標簽之間的內(nèi)容將會根據(jù)數(shù)組大小進行迭代,并且當數(shù)組元素為對象時,還會把該對象作為每一次迭代的上下文,以便迭代時的標簽可以直接引用數(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>

從這個測試結(jié)果中可以看到,{{#prop}}{{/prop}}之間的模板內(nèi)容根據(jù)prop所引用的數(shù)組迭代了兩次,并且在這對標簽內(nèi)部直接通過{{name}}標簽,輸出了數(shù)組元素對象上的name屬性對應的值。

如果prop屬性所引用的是一個函數(shù),但是這個函數(shù)返回值是一個數(shù)組類型,那么仍然會進行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) 動態(tài)渲染

當prop屬性所引用的是一個函數(shù),并且這個函數(shù)的返回值還是一個函數(shù)的話,mustache會再次調(diào)用這個返回的函數(shù),并給它傳遞2個參數(shù):text表示原來的模板內(nèi)容,render表示mustache內(nèi)部的執(zhí)行渲染的對象,以便在這個函數(shù)內(nèi)部可以通過這render對象,結(jié)合原來的模板內(nèi)容,自定義渲染的邏輯,并把函數(shù)的返回值作為渲染結(jié)果(這個返回值渲染的邏輯跟{{prop}}標簽完全一樣):

<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}}標簽
這對標簽,與{{#prop}}{{/prop}}的if-else渲染執(zhí)行相反邏輯,即只有在prop屬性不存在,或者引用的是一個falsy值,或者是一個空數(shù)組的時候才會顯示標簽之間的內(nèi)容,否則不會顯示:

<script id="tpl2" type="text/html">
 -{{^prop}}content{{/prop}}-
</script>
<script>
 var tpl2 = document.getElementById('tpl2').innerHTML.trim();
 Mustache.parse(tpl2);
 //測試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-
 // 測試空數(shù)組
 console.log(Mustache.render(tpl2, {prop: []}));//-content-
 // 測試不存在的屬性
 console.log(Mustache.render(tpl2, {prop2: true}));//-content-
 //測試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-


 //測試簡單對象
 console.log(Mustache.render(tpl2, {prop: {name: 'jason'}}));//--
 //測試日期對象
 console.log(Mustache.render(tpl2, {prop: new Date()}));//--
 // 測試非空數(shù)組
 console.log(Mustache.render(tpl2, {prop: [{name: 'jason'},{name: 'tom'}]}));//--

 //測試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'}));//--
 
 //測試返回非falsy,非空數(shù)組的function
 console.log(Mustache.render(tpl2, {
  prop: function () {
   return 'it\'s a fun'
  }
 }));//--

 //測試返回function的function
 console.log(Mustache.render(tpl2, {
  prop: function () {
   return function(text,render){
    return '<b>' + render(text) +'</b>'
   }
  }
 }));//--
</script>

7. 渲染上下文
mustache有一個渲染上下文棧的概念,在模板渲染的開始的時候,把數(shù)據(jù)源對象作為當前的渲染上下文 ,并壓入上下文棧。在遇到{{#prop}}標簽的時候,如果prop引用的是一個對象或者是一個非空的對象數(shù)組,或者prop引用的是一個函數(shù),并且這個函數(shù)返回的是一個對象或者是一個非空的對象數(shù)組,就會把這個對象或者數(shù)組的元素作為當前渲染上下文,并壓入上下文棧,當這個標簽渲染完畢的時候,才會把該上下文彈出,恢復上一層標簽所使用的上下文。由于{{#prop}}標簽可以多層嵌套,所以在有的模板渲染的時候,會有多層上下文存在。mustache在解析標簽時,根據(jù)標簽名稱查找當前上下文對象是否存在該屬性,如果不存在就會到上層上下文對象中查找,只要在某一層找到,就會用該層上下文對象的值來渲染。

<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>

上面這個例子中,在渲染{{#address}}{{/address}}時,上下文對象已經(jīng)變成了obj2.person.student.address所引用的對象,所以{{home}}渲染時用到的就是obj2.person.student.address.home屬性,而{{age}}渲染時,由于obj2.person.student.address不存在age屬性,所以就會到上層上下文中查找,一直到obj2對象才找到,于是就把obj2.age當成了渲染結(jié)果。

還有一種方法,不用通過{{#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>

這種方法其實很好理解,只要知道當前的上下文對象,再根據(jù)屬性操作串person.student.address.home,當然就能找到需要的值了。

本文介紹了一個非常好用的前端模板引擎,涵蓋的內(nèi)容包含了在日常工作肯定會用到的知識點,希望大家喜歡。

相關文章

最新評論