Underscore _.template 方法使用詳解

https://github.com/hanzichi/underscore-analysis/issues/26javascript

前文 淺談 Web 中先後端模板引擎的使用 咱們簡單瞭解了模板引擎在先後端的應用場景,本文重點深刻 Underscore 的模板函數 _.template,來看看它的用法以及實現原理。html

from simplest

咱們從 官方文檔 中最簡單的例子提及。前端

var compiled = _.template("hello: <%= name %>");
var html = compiled({name: 'moe'}); // hello: moe

{name: 'moe'} 模擬後臺請求到的接口數據,而變量 html 則爲拼接成的字符串,以後即可以用 innerHTML 方法加入到頁面生成 DOM。java

這一切是如何作到的?咱們能夠打印看下 compliled 方法是個什麼樣子(須要去 Underscore 源碼中打印)。git

大概是這個樣子(其實不徹底準確,真實的應該還會有個 _ 參數傳入,使得函數能用 Underscore 內部方法):github

function(obj){
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__p+='hello: '+
((__t=( name ))==null?'':__t)+
'';
}
return __p;
}

仔細想一想,其實就是對模板字符串進行了正則解析,將須要填入數據的位置預留出來,拼接成一個字符串,用 new Function 構造一個方法(動態執行 JavaScript 字符串),方法中有大量的字符串拼接過程,而後將數據代入這個方法,返回咱們須要的 HTML 字符串。後端

盜用 木神 兩張圖,過程很是清晰。ide

1

2

三種模板

_.template 支持如下三種模板。函數

1. <%  %> - to execute some code
2. <%= %> - to print some value in template
3. <%- %> - to print some values HTML escaped

<% %> 裏包裹的是一些可執行的 JavaScript 語句,好比 if-else 語句,for 循環語句,等等。<%= %> 正是咱們前面使用的,會打印傳入數據相應的 key 的值,<%- %> 和前者相比,多了步 HTML 實體編碼的過程,能夠有效防止 XSS 攻擊。性能

舉個栗子:

<div></div>
<script src="underscore.js"></script>
<script type="text/template" id="tpl">
  <ul class="list">
    <% _.each(obj, function(e, i, a){ %>
      <% if (i === 0) %>
        <li><%- e.name %>
      <% else if (i === a.length - 1) %>
        <li class="last-item"><%= e.name %></li>
      <% else %>
        <li><%= e.name %></li>
    <% }) %>
  </ul>
</script>
<script>
// mock data
var data = [{name: "<script>"}, {name: "orange"}, {name: "peach"}];

var compiled = _.template(document.getElementById("tpl").innerHTML);
var html = compiled(data);
// console.log(html)
document.querySelector("div").innerHTML = html;
</script>

將數據用 li 標籤循環展現,而且將第一個值實體編碼了。

其餘功能

_.template 最基礎的應用就是這樣。

若是你不喜歡它默認的模板風格,也能夠本身定義,注意 key 必須和源碼中的 key 保持一致,才能覆蓋。

_.templateSettings = {
  // 三種渲染模板
  evaluate    : /<%([\s\S]+?)%>/g,
  interpolate : /<%=([\s\S]+?)%>/g,
  escape      : /<%-([\s\S]+?)%>/g
};

有兩種方式,一種是直接修改 _.templateSettings 變量(不推薦,修改了源碼中的變量)

_.templateSettings = {
  interpolate: /\{\{(.+?)\}\}/g 
};

var template = _.template("Hello {{ name }}!");
var ans = template({name: "Mustache"});
console.log(ans); // Hello Mustache!

比較好的方法是做爲 _.template 的第二個參數 settings 傳入:

var settings = {
  interpolate: /\{\{(.+?)\}\}/g	 // 覆蓋 _.templateSettings.interpolate
};

var template = _.template("Hello {{ name }}!", settings);
var ans = template({name: "Mustache"});
console.log(ans); // Hello Mustache!

咱們還能設定 settings.variable 指定 scope:

var template = _.template("Using 'with': <%= data.answer %>", {variable: 'data'})
var ans = template({answer: 'no'});
console.log(ans)  // Using 'with': no

預編譯

模板引擎通常都帶有預編譯功能,_.template 也不例外。

什麼是預編譯?有什麼用?

上面的代碼有兩個痛點:

  1. 性能:模板引擎渲染的時候依賴 Function 構造器實現,Function 與 eval、setTimeout、setInterval 同樣,提供了使用文本訪問 javascript 解析引擎的方法,但這樣執行 javascript 的性能很是低下。

  2. 調試:因爲是動態執行字符串,若遇到錯誤調試器沒法捕獲錯誤源,致使模板 BUG 調試變得異常痛苦。在沒有進行容錯的引擎中,局部模板若由於數據異常甚至能夠致使整個應用崩潰,隨着模板的數目增長,維護成本將劇增。

若是咱們 JavaScript 代碼中直接保存 _.template 的結果,那麼以上兩個問題就不復存在。而 _.template(jstText).source 則保存了 _.template(jstText) 返回的方法字符串。

JST is a server-side thing, not client-side. This mean that you compile Unserscore template on server side by some server-side script and save the result in a file. Then use this file as compiled Unserscore template.

小結

關於 _.template 方法的具體實現,能夠參考樓主的 underscore-1.8.3.js 源碼解讀全文註釋版 ,全局搜索便可。

關於前端模板引擎,其實樓主也是個初學者,學習過程當中搜到的資料與你們分享下,有機會必定要用下各類模板引擎而後分析下。

相關文章
相關標籤/搜索