EJS 模板快速入門

Node 開源模板的選擇很多,但推薦像我這樣的老人去用 EJS,有 Classic ASP/PHP/JSP 的經驗用起 EJS 來的確可以很自然,也就是說,你能夠在 <%...%> 塊中安排 JavaScript 代碼,利用最傳統的方式 <%=輸出變量%>(另外 <%-輸出變量是不會對 & 等符號進行轉義的)。安裝 EJS 命令如下:

npm install ejs

JS 調用

JS 調用的方法主要有兩個:

[javascript] view plaincopy
  1. <code>ejs.compile(str, options);  
  2. // => Function  
  3.   
  4. ejs.render(str, options);  
  5. // => str</code>  

實際上 EJS 可以遊離於 Express 獨立使用的,例如:

[javascript] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. var ejs = require(''), str = require('fs').readFileSync(__dirname + '/list.ejs''utf8');  
  2.   
  3. var ret = ejs.render(str, {  
  4.   names: ['foo''bar''baz']  
  5. });  
  6.   
  7. console.log(ret);  
見 ejs.render(),第一個參數是 模板 的字符串,模板如下。
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <% if (names.length) { %>  
  2.   <ul>  
  3.     <% names.forEach(function(name){ %>  
  4.       <li foo='<%= name + "'" %>'><%= name %></li>  
  5.     <% }) %>  
  6.   </ul>  
  7. <% } %>  

names 成了本地變量。

選項參數

第二個參數是數據,一般是一個對象。而這個對象又可以視作爲選項,也就是說數據和選擇都在同一個對象身上。

如果不想每次都都磁盤,可需要緩存模板,設定 options.filename  即可。例如:

[javascript] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. var ejs = require('../')  
  2.   , fs = require('fs')  
  3.   , path = __dirname + '/functions.ejs'  
  4.   , str = fs.readFileSync(path, 'utf8');  
  5.   
  6. var users = [];  
  7.   
  8. users.push({ name: 'Tobi', age: 2, species: 'ferret' })  
  9. users.push({ name: 'Loki', age: 2, species: 'ferret' })  
  10. users.push({ name: 'Jane', age: 6, species: 'ferret' })  
  11.   
  12. var ret = ejs.render(str, {  
  13.   users: users,  
  14.   filename: path  
  15. });  
  16.   
  17. console.log(ret);  

相關選項如下:

  • cache Compiled functions are cached, requires filename
  • filename 緩存的鍵名稱
  • scope 函數執行的作用域
  • debug Output generated function body
  • compileDebug When false no debug instrumentation is compiled
  • client Returns standalone compiled function

inculde 指令

而且,如果要如

<ul>
  <% users.forEach(function(user){ %>
    <% include user/show %>
  <% }) %>
</ul>
般插入公共模板,也就是引入文件,必須要設置 filename 選項才能啓動 include 特性,不然 include 無從知曉所在目錄。

模板:

[javascript] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <h1>Users</h1>  
  2.   
  3. <% function user(user) { %>  
  4.   <li><strong><%= user.name %></strong> is a <%= user.age %> year old <%= user.species %>.</li>  
  5. <% } %>  
  6.   
  7. <ul>  
  8.   <% users.map(user) %>  
  9. </ul>  
EJS 支持編譯模板。經過模板編譯後就沒有 IO 操作,會非常快,而且可以公用本地變量。下面例子 user/show 忽略 ejs 擴展名:
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <ul>  
  2.   <% users.forEach(function(user){ %>  
  3.     <% include user/show %>  
  4.   <% }) %>  
  5. </ul>  

自定義 CLOSE TOKEN

如果打算使用 <h1>{{= title }}</h1> 般非 <%%>標識,也可以自定義的。

[javascript] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. var ejs = require('ejs');  
  2. ejs.open = '{{';  
  3. ejs.close = '}}';  
格式化輸出也可以哦。
[javascript] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. ejs.filters.last = function(obj) {  
  2.   return obj[obj.length - 1];  
  3. };  
調用
[html] view plaincopy
  1. <p><%=: users | last %></p>  
EJS 也支持瀏覽器環境。
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <html>  
  2.   <head>  
  3.     <script src="../ejs.js"></script>  
  4.     <script id="users" type="text/template">  
  5.       <% if (names.length) { %>  
  6.         <ul>  
  7.           <% names.forEach(function(name){ %>  
  8.             <li><%= name %></li>  
  9.           <% }) %>  
  10.         </ul>  
  11.       <% } %>  
  12.     </script>  
  13.     <script>  
  14.       onload = function(){  
  15.         var users = document.getElementById('users').innerHTML;  
  16.         var names = ['loki', 'tobi', 'jane'];  
  17.         var html = ejs.render(users, { names: names });  
  18.         document.body.innerHTML = html;  
  19.       }  
  20.     </script>  
  21.   </head>  
  22.   <body>  
  23.   </body>  
  24. </html>  

不知道 EJS 能否輸出多層 JSON 對象呢?

對了,有網友爆料說,jQ 大神 John 若干年前寫過 20 行的模板,汗顏,與 EJS 相似但短小精悍!

這裏順便貼一個高手寫的(http://www.jiangkunlun.com/2012/05/js_%E6%A8%A1%E6%9D%BF/):

簡單實用的js模板引擎

不足 50 行的 js 模板引擎,支持各種 js 語法:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <script id="test_list" type="text/html">  
  2. <%=  
  3.     for(var i = 0l = p.list.length; i < l; i++){  
  4.         var stu = p.list[i];  
  5. =%>  
  6.     <tr>  
  7.         <td<%=if(i==0){=%> class="first"<%=}=%>><%==stu.name=%></td>  
  8.         <td><%==stu.age=%></td>  
  9.         <td><%==(stu.address || '')=%></td>  
  10.     <tr>  
  11.    
  12. <%=  
  13.     }  
  14. =%>  
  15. </script>  
“<%= xxx =%>”內是 js 邏輯代碼,“<%== xxx =%>”內是直接輸出的變量,類似 php 的 echo 的作用。“p”是調用下面 build 方法時的 k-v 對象參數,也可以在調用 “new JTemp” 時設置成別的參數名

調用:
[javascript] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. $(function(){  
  2.     var temp = new JTemp('test_list'),  
  3.         html = temp.build(  
  4.             {list:[  
  5.                    {name:'張三', age:13, address:'北京'},  
  6.                 {name:'李四', age:17, address:'天津'},  
  7.                 {name:'王五', age:13}  
  8.             ]});  
  9.     $('table').html(html);  
  10. });  
上面的 temp 生成以後,可以多次調用 build 方法,生成 html。以下是模板引擎的代碼:
[javascript] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. var JTemp = function(){  
  2.     function Temp(htmlId, p){  
  3.         p = p || {};//配置信息,大部分情況可以缺省  
  4.         this.htmlId = htmlId;  
  5.         this.fun;  
  6.         this.oName = p.oName || 'p';  
  7.         this.TEMP_S = p.tempS || '<%=';  
  8.         this.TEMP_E = p.tempE || '=%>';  
  9.         this.getFun();  
  10.     }  
  11.     Temp.prototype = {  
  12.         getFun : function(){  
  13.             var _ = this,  
  14.                 str = $('#' + _.htmlId).html();  
  15.             if(!str) _.err('error: no temp!!');  
  16.             var str_ = 'var ' + _.oName + '=this,f=\'\';',  
  17.                 s = str.indexOf(_.TEMP_S),  
  18.                 e = -1,  
  19.                 p,  
  20.                 sl = _.TEMP_S.length,  
  21.                 el = _.TEMP_E.length;  
  22.             for(;s >= 0;){  
  23.                 e = str.indexOf(_.TEMP_E);  
  24.                 if(e < s) alert(':( ERROR!!');  
  25.                 str_ += 'f+=\'' + str.substring(0, s) + '\';';  
  26.                 p = _.trim(str.substring(s+sl, e));  
  27.                 if(p.indexOf('=') !== 0){//js語句  
  28.                     str_ += p;  
  29.                 }else{//普通語句  
  30.                     str_ += 'f+=' + p.substring(1) + ';';  
  31.                 }  
  32.                 str = str.substring(e + el);  
  33.                 s = str.indexOf(_.TEMP_S);  
  34.             }  
  35.             str_ += 'f+=\'' + str + '\';';  
  36.             str_ = str_.replace(/\n/g, '');//處理換行  
  37.             var fs = str_ + 'return f;';  
  38.             this.fun = Function(fs);  
  39.         },  
  40.         build : function(p){  
  41.             return this.fun.call(p);  
  42.         },  
  43.         err : function(s){  
  44.             alert(s);  
  45.         },  
  46.         trim : function(s){  
  47.             return s.trim?s.trim():s.replace(/(^\s*)|(\s*$)/g,"");  
  48.         }  
  49.     };  
  50.     return Temp;  
  51. }();  

核心是將模板代碼轉變成了一個拼接字符串的 function,每次拿數據 call 這個 function。

因爲主要是給手機(webkit)用的,所以沒有考慮字符串拼接的效率問題,如果需要給 IE 使用,最好將字符串拼接方法改爲 Array.push() 的形式。

網友作品:cocoTemplate


附:connect + ejs 的一個例子。

[javascript] view plaincopy
  1. var Step = require('../../libs/step'),  
  2.     _c  = require('./utils/utils'),  
  3.     fs  = require('fs'),  
  4.     ejs = require('ejs'),  
  5.     connect = require('connect');  
  6.   
  7. exports.loadSite = function(request, response){  
  8.     var siteRoot = 'C:/代碼存檔/sites/a.com.cn';  
  9.     // _c.log(request.headers.host);  
  10.       
  11.     var url = request.url;  
  12.     // 如果有 html 的則是動態網頁,否則爲靜態內容  
  13.     if(url == '/' || ~url.indexOf('/?') || url.indexOf('.asp') != -1 || url[url.length - 1] == '/'){  
  14.         var tplPath;  
  15.           
  16.         if(url == '/' || ~url.indexOf('/?') || url[url.length - 1] == '/'){  
  17.             // 默認 index.html  
  18.             tplPath = siteRoot + request.url + 'default.asp';  
  19.         }else{  
  20.             tplPath = siteRoot + request.url.replace(/\?.*$/i,''); // 只需要文件名  
  21.         }  
  22.   
  23.         // 從文件加載模板  
  24.         Step(function(){  
  25.             _c.log('加載模板:' + tplPath);  
  26.             fs.exists(tplPath, this);  
  27.         }, function(path_exists){  
  28.             if(path_exists === true)fs.readFile(tplPath, "utf8"this);  
  29.             else if(path_exists === false) response.end404(request.url);  
  30.             else response.end500('文件系統異常''');  
  31.         },function(err, tpl){  
  32.   
  33.             var bigfootUrl, cssUrl, projectState = 0; // 0 = localhot/ 1 = Test Server / 2 = Deployed  
  34.             switch(projectState){  
  35.                 case 0:  
  36.                      bigfootUrl  = "http://127.0.0.1/bigfoot/";  
  37.                      cssUrl      = "http://127.0.0.1/lessService/?isdebug=true";  
  38.                 break;    
  39.                 case 1:  
  40.                      bigfootUrl  = "http://112.124.13.85:8080/static/";  
  41.                      cssUrl      = "/asset/style/";  
  42.                 break;    
  43.                 case 2:  
  44.                      bigfootUrl  = "http://localhost:8080/bigfoot/";  
  45.                 break;  
  46.             }  
  47.   
  48.             var sitePath = request.getLevelByUrl(require(siteRoot + '/public/struct')),  
  49.                 first = sitePath[0];  
  50.             var htmlResult = ejs.render(tpl, {  
  51.                 filename : tplPath,  
  52.                 bigfootUrl: bigfootUrl,  
  53.                 cssUrl : cssUrl,  
  54.                 projectState: projectState,  
  55.                 query_request: request.toJSON(),  
  56.                 request: request,  
  57.                 config: require(siteRoot + '/public/config'),  
  58.                 struct: require(siteRoot + '/public/struct'),  
  59.                 sitePath : sitePath,  
  60.                 firstLevel : first  
  61.             });  
  62.             // _c.log(first.children.length)  
  63.             response.end200(htmlResult);  
  64.         });  
  65.           
  66.     }else{  
  67.         connect.static(siteRoot)(request, response, function(){  
  68.             // if not found...  
  69.             response.writeHead(404, {'Content-Type''text/html'});  
  70.             response.end('404');      
  71.         });  
  72.     }  
  73. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章