基於MVC模式的web框架在渲染頁面時,都會提供能夠內嵌後端語言的模板引擎,用於使用動態數據生成頁面。在某些場景下,沒法使用後端的模板引擎,但又須要使用動態數據渲染頁面內容,這時即可選擇基於Javascript的模板引擎。javascript
背景:以前公司有一套C/S產品線,後來爲了產品的web化,在中間件上封裝了一個Restful service接口,用以響應web前端的數據請求。前端只提供了供Javascript調用的數據接口,返回的數據格式爲序列化的JSON。所以頁面的渲染只能在前端,由Javascript調用Restful service接口得到動態數據以後才能進行。html
基於這種場景,考慮尋找一個Javascript的模板引擎進行頁面渲染。因爲本次需求(一個網站)相對簡單,所以沒有選擇功能強大且複雜的模板引擎,而是採用了一個極其簡單的引擎。前端
這個模板引擎是John Resig在幾年前寫的(這裏),代碼很是簡潔。jquery以前有幾個版本提供了模板引擎的功能,後來又去掉了,我沒有實際用過jquery的模板功能,只摟了一眼使用方式,猜想跟這個模板引擎應該是有淵源的。John在他的書《 Secrets of the JavaScript Ninja》裏也對這段代碼作了介紹。下面是這個模板引擎的所有代碼:java
/* * javascript template from John Resig * @see : http://ejohn.org/blog/javascript-micro-templating */ (function () { var cache = {}; this.tmpl = function tmpl (str, data) { var fn = !/\W/.test(str) ? cache[str] = cache[str] || tmpl(document.getElementById(str).innerHTML) : new Function ("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + "with(obj){p.push('" + str .replace(/[\r\t\n]/g, " ") .split("<%").join("\t") .replace(/((^|%>)[^\t]*)'/g, "$1\r") .replace(/\t=(.*?)%>/g, "',$1,'") .split("\t").join("');") .split("%>").join("p.push('") .split("\r").join("\\'") + "');}return p.join('');"); return data ? fn(data) : fn; }; })();
代碼只提供了一個tmpl()函數,函數內部進行的是字符串操做,把data填入模板而後生成一段html字符串。
jquery
一個模板示例:web
<script type="text/template" id="actsListTmpl"> <div class="contContainer"> <h2 class="h2_bg">活動</h2> <ul id="allActs" class="arrowlist f14"> <% if (acts.length === 0) { %> <li>暫無活動</li> <% } %> <% for ( var i = 0; i < acts.length; i++ ) { %> <li> <span class="rightText"> <div class="jobOvsiteTruncate" title="<%=acts[i].site%>"> <%=acts[i].site%> </div> </span> <a name="<%=acts[i].ID%>" href="#"> <div class="jobOvnameTruncate" title="<%=acts[i].ActiName%>"> <%=acts[i].ActiName%> </div> </a> </li> <% } %> </ul> </div> </script>
模板被寫在一個<script>標籤裏面,注意<script>標籤的type="text/template",這並非標準的content-type。當瀏覽器檢測到內嵌<script>的type爲不能識別的content-type時,會忽略掉其中的內容,所以這段代碼既不會被頁面渲染也不會被當作腳原本執行。這提供了咱們一種在頁面中巧妙地內嵌輕量級模板的方法。ajax
模板中內容的寫法,相似MVC框架中模板引擎的寫法,採用<%= %>引入變量,使用<%%>引入Javascript的分支、循環等邏輯,這已經知足了基本的模板引擎應用。後端
使用上面的模板生成html內容:瀏覽器
var actsData = { acts: [ {ID: "xxx", ActiName: "xxxxxx", site: "xxxx"}, {ID: "xxx", ActiName: "xxxxxx", site: "xxxx"}, {ID: "xxx", ActiName: "xxxxxx", site: "xxxx"} ] }; tmpl("actsListTmpl", actsData);
其中"actsListTmpl"是爲模板的<script>標籤指定的ID,actsData是構造的代入模板的數據對象。tmpl方法會返回一串html代碼,咱們只需將這段html插入頁面想要的位置就能夠了:app
<div id="actsList"></div>
var acts = document.getElementById("actsList"); acts.innerHTML = tmpl("actsListTmpl", actsData);
如上,咱們能夠把模板預置在頁面中,使用ajax方式獲取動態數據,而後使用tmpl方法渲染出最終的頁面。
項目中遇到的一個問題是,有個模板須要在不一樣的頁面上使用。所以便把這個模板抽離出來,單獨寫在了一個html文件中,在須要使用這個模板的頁面上只寫了一個script標籤,準備在加載頁面時把模板內容動態加載到script標籤中:
模板:
<table class="ListTable"> <thead> <tr> <th class="CollectColumn"></th> <th class="NameColumn">名稱</th> <th class="SiteColumn">地點</th> <th>類別</th> </tr> </thead> // 省略
頁面標籤:
<script type="text/template" id="fullListTmpl"></script>
而後加載頁面時,把模板內容加載到script標籤中:
$("#fullListTmpl").load("listTemplate.html", function () { // callback });
這時遇到的一個問題是: IE8中不容許向script標籤appendChild,所以不得已在IE8時,使用了一個隱藏的div元素做爲模板的容器:
<!--[if lte IE 8]> <div style="display: none;" id="fullListTmpl"></div> <![endif]--><!-- cann't append child to <script> tag in IE8 --> <script type="text/template" id="fullListTmpl"></script>
由於時間緣由沒有作過多考慮,這裏也許會有更好的解決方法。
須要注意的是,這段代碼實現的是最簡單的模板引擎功能,適用於只須要簡單的頁面渲染功能的應用場景,對於一些較複雜的頁面可能並不適用。若須要實現較複雜的前端渲染,應該考慮功能更完善的模板引擎或一些前端MVVM框架。這段模板引擎很是精巧,代碼也很簡潔,在實際使用的時候能夠根據需求對其進行修改,以知足須要。以這段代碼爲基礎,能夠擴展出一個功能很是強大的Javascript模板引擎。若是你打算在項目中使用這個小巧的模板引擎,請作充分的全局考慮,看它是否可以知足項目的需求。