Node 開源模板的選擇很多,但推薦像我這樣的老人去用EJS,有Classic ASP/PHP/JSP 的經驗用起EJS 來的確可以很自然,也就是說,你能夠在<%...%> 塊中安排JavaScript 代碼,利用最傳統的方式<%=輸出變量%>(另外<%-輸出變量是不會對& 等符號進行轉義的)。安裝EJS 命令如下:
npm install ejs
JS 調用
JS 調用的方法主要有兩個:
ejs.compile(str, options); // => Function ejs.render(str, options); // => str
實際上EJS 可以游離於Express 獨立使用的,例如:
var ejs = require(''), str = require('fs').readFileSync(__dirname + '/list.ejs', 'utf8'); var ret = ejs.render(str, { names: ['foo', 'bar', 'baz'] }); console.log(ret); 見ejs.render(),第一個參數是模板的字符串,模板如下。 <% if (names.length) { %> <ul> <% names.forEach(function(name){ %> <li foo='<%= name + "'" %>'><%= name %></li> <% }) %> </ul> <% } %>names 成了本地變量。
選項參數
第二個參數是數據,一般是一個對象。而這個對象又可以視作為選項,也就是說數據和選擇都在同一個對象身上。
如果不想每次都都磁盤,可需要緩存模板,設定options.filename 即可。例如:
var ejs = require('../') , fs = require('fs') , path = __dirname + '/functions.ejs' , str = fs.readFileSync(path, 'utf8'); var users = []; users.push({ name: 'Tobi', age: 2, species: 'ferret' }) users.push({ name: 'Loki', age: 2, species: 'ferret' }) users.push({ name: 'Jane', age: 6, species: 'ferret' }) var ret = ejs.render(str, { users: users, filename: path }); console.log(ret);inculde 指令
而且,如果要如
<ul> <% users.forEach(function(user){ %> <% include user/show %> <% }) %></ul>般插入公共模板,也就是引入文件,必須要設置filename 選項才能啟動include 特性,不然include 無從知曉所在目錄。
模板:
<h1>Users</h1> <% function user(user) { %> <li><strong><%= user.name %></strong> is a <%= user.age %> year old <%= user.species %>.</li> <% } %> <ul> <% users.map(user) %> </ul>EJS 支持編譯模板。經過模板編譯後就沒有IO 操作,會非常快,而且可以公用本地變量。下面例子user/show 忽略ejs 擴展名:
<ul> <% users.forEach(function(user){ %> <% include user/show %> <% }) %> </ul>自定義CLOSE TOKEN
如果打算使用<h1>{{= title }}</h1> 般非<%%>標識,也可以自定義的。
var ejs = require('ejs'); ejs.open = '{{'; ejs.close = '}}';格式化輸出也可以哦。 ejs.filters.last = function(obj) { return obj[obj.length - 1]; };調用:<p><%=: users | last %></p>EJS 也支持瀏覽器環境。
<html> <head> <script src="../ejs.js"></script> <script id="users" type="text/template"> <% if (names.length) { %> <ul> <% names.forEach(function(name){ %> <li><%= name %></li> <% }) %> </ul> <% } %> </script> <script> onload = function(){ var users = document.getElementById('users').innerHTML; var names = ['loki', 'tobi', 'jane']; var html = ejs.render(users, { names: names }); document.body.innerHTML = html; } </script> </head> <body> </body> </html>不知道EJS 能否輸出多層JSON 對象呢?對了,有網友爆料說,jQ 大神John 若干年前寫過20 行的模板,汗顏,與EJS 相似但短小精悍!
簡單實用的js模板引擎
不足50 行的js 模板引擎,支持各種js 語法:
<script id="test_list" type="text/html"> <%= for(var i = 0, l = p.list.length; i < l; i++){ var stu = p.list[i]; =%> <tr> <td<%=if(i==0){=%><%=}=%>><%==stu.name=%></td> <td><%==stu.age=%></td> <td><%==(stu.address || '')=%></td> <tr> <%= } =%> </script>“<%= xxx =%>”內是js 邏輯代碼,“<%== xxx =%>”內是直接輸出的變量,類似php 的echo 的作用。 “p”是調用下面build 方法時的kv 對象參數,也可以在調用“new JTemp” 時設置成別的參數名
調用:
$(function(){ var temp = new JTemp('test_list'), html = temp.build( {list:[ {name:'張三', age:13, address:'北京'}, {name:'李四', age:17, address:'天津'}, {name:'王五', age:13} ]}); $('table').html(html); });上面的temp 生成以後,可以多次調用build 方法,生成html。以下是模板引擎的代碼:
var JTemp = function(){ function Temp(htmlId, p){ p = p || {};//配置信息,大部分情況可以缺省this.htmlId = htmlId; this.fun; this.oName = p.oName || 'p'; this.TEMP_S = p.tempS || '<%='; this.TEMP_E = p.tempE || '=%>'; this.getFun(); } Temp.prototype = { getFun : function(){ var _ = this, str = $('#' + _.htmlId).html(); if(!str) _.err('error: no temp!!'); var str_ = 'var ' + _.oName + '=this,f=/'/';', s = str.indexOf(_.TEMP_S), e = -1, p, sl = _.TEMP_S.length, el = _.TEMP_E.length; for(;s >= 0;){ e = str.indexOf(_.TEMP_E); if(e < s) alert(':( ERROR!!'); str_ += 'f+=/'' + str.substring(0, s) + '/';'; p = _.trim(str.substring(s+sl, e)); if(p.indexOf('=') !== 0){//js語句str_ += p; }else{//普通語句str_ += 'f+=' + p.substring(1) + ';'; } str = str.substring(e + el); s = str.indexOf(_.TEMP_S); } str_ += 'f+=/'' + str + '/';'; str_ = str_.replace(//n/g, '');//處理換行var fs = str_ + 'return f;'; this.fun = Function(fs); }, build : function(p){ return this.fun.call(p); }, err : function(s){ alert(s); }, trim : function(s){ return s.trim?s.trim():s.replace(/(^/s*)|(/s*$)/g,""); } }; return Temp; }();核心是將模板代碼轉變成了一個拼接字符串的function,每次拿數據call 這個function。
因為主要是給手機(webkit)用的,所以沒有考慮字符串拼接的效率問題,如果需要給IE 使用,最好將字符串拼接方法改為Array.push() 的形式。
ejs模板佈局layout
1. 如果不願意使用默認的layout.ejs,可自行指定。例如:
res.render("index",{"title":"test","layout":"main"});// 或res.render("index",{"title":"test","layout":"main.ejs"});2. 如果不願意使用layout,則可以設置layout為false,例如:
res.render("index",{"layout":false});3. 如果不想每個請求都單獨設置一次。可以使用全局設置:
app.set("view options",{ "layout":false});4. ejs 裡,默認的閉合標記是<% .. %>,我們也可以定義自己的標籤。例如:
app.set("view options",{ "open":"{{", "close":"}}"});5. 局部佈局
在web應用中,經常會需要重複顯示某個內容,例如:用戶評論功能,需要重複顯示出每一條用戶的評論,這個時候,我們可以通過循環來實現。但是也可以使用【局部模版】( partial)來實現。例如:
首先我們建一個局部的模版./views/comment.ejs:
<div> <div><%=comment.user%></div> <div><%=comment.content%></div> </div>
注意:這裡是comment.xxxx
然後在./views/index.ejs中,通過partial調用comment
this is <%=title%>! <br/> <%- partial("comment", comments)%>注意:這裡是partial("comment.ejs", comments); <-- 單詞要用複數。
最後是在router中,調用index.ejs。
app.get("/",function(req,res){ res.render("index",{"title":"test","layout":false,"comments":[ {"user":"gainover","content":"test1"}, {"user":"zongzi","content":"test2"}, {"user":"maomao","content":"test3"} ]}); });注意:代碼裡的comments 和index.ejs的comments變量名稱一致,而partial所調用的comment.ejs中,則採用comment 的單數形式。
在列表顯示時,我們通常會遇到的場景是,對第一個元素或者最後一個元素加以特殊顯示。在partial中,我們可以通過express內置的變量來判斷當前對像是否是第一個元素或者最後一個元素,例如:
<div> <div><%=comment.user%></div> : <div><%=comment.content%></div> </div>
這樣第一條評論的class 裡就會多一個firstitem。
類似的內置變量還有:
(1)firstInCollection 如果是數組的第一個元素,則為true
(2)indexInCollection 當前元素在數組裡的索引
(3)lastInCollection 如果是數組的最後一個元素,則為true
(4)collectionLength 數組的長度
最後是partial調用模版時的路徑查找問題:
(1)partial("edit") 會查找同目錄下的edit.ejs文件。
(2)partial("../message") 會查找上一級目錄的message.ejs文件。
(3)partial("users") 會查找users.ejs文件,如果不存在users.ejs, 則會查找/users/index.ejs文件。
(4)<%= users %> 會對內容進行轉義,想不轉義,可以用<%- users %>