Node 開源模板的選擇很多,但推薦像我這樣的老人去用 EJS,有 Classic ASP/PHP/JSP 的經驗用起 EJS 來的確可以很自然,也就是說,你能夠在 <%...%> 塊中安排 JavaScript 代碼,利用最傳統的方式 <%=輸出變量%>(另外 <%-輸出變量是不會對 & 等符號進行轉義的)。安裝 EJS 命令如下:
npm install ejs
JS 調用
JS 調用的方法主要有兩個:
- <code>ejs.compile(str, options);
- // => Function
- ejs.render(str, options);
- // => str</code>
實際上 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);
- <% 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);
相關選項如下:
cache
Compiled functions are cached, requiresfilename
filename
緩存的鍵名稱scope
函數執行的作用域debug
Output generated function bodycompileDebug
Whenfalse
no debug instrumentation is compiledclient
Returns standalone compiled function
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>
- <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>
- <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 相似但短小精悍!
這裏順便貼一個高手寫的(http://www.jiangkunlun.com/2012/05/js_%E6%A8%A1%E6%9D%BF/):
簡單實用的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){=%> class="first"<%=}=%>><%==stu.name=%></td>
- <td><%==stu.age=%></td>
- <td><%==(stu.address || '')=%></td>
- <tr>
- <%=
- }
- =%>
- </script>
調用:
- $(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);
- });
- 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() 的形式。
網友作品:cocoTemplate
附:connect + ejs 的一個例子。
- var Step = require('../../libs/step'),
- _c = require('./utils/utils'),
- fs = require('fs'),
- ejs = require('ejs'),
- connect = require('connect');
- exports.loadSite = function(request, response){
- var siteRoot = 'C:/代碼存檔/sites/a.com.cn';
- // _c.log(request.headers.host);
- var url = request.url;
- // 如果有 html 的則是動態網頁,否則爲靜態內容
- if(url == '/' || ~url.indexOf('/?') || url.indexOf('.asp') != -1 || url[url.length - 1] == '/'){
- var tplPath;
- if(url == '/' || ~url.indexOf('/?') || url[url.length - 1] == '/'){
- // 默認 index.html
- tplPath = siteRoot + request.url + 'default.asp';
- }else{
- tplPath = siteRoot + request.url.replace(/\?.*$/i,''); // 只需要文件名
- }
- // 從文件加載模板
- Step(function(){
- _c.log('加載模板:' + tplPath);
- fs.exists(tplPath, this);
- }, function(path_exists){
- if(path_exists === true)fs.readFile(tplPath, "utf8", this);
- else if(path_exists === false) response.end404(request.url);
- else response.end500('文件系統異常', '');
- },function(err, tpl){
- var bigfootUrl, cssUrl, projectState = 0; // 0 = localhot/ 1 = Test Server / 2 = Deployed
- switch(projectState){
- case 0:
- bigfootUrl = "http://127.0.0.1/bigfoot/";
- cssUrl = "http://127.0.0.1/lessService/?isdebug=true";
- break;
- case 1:
- bigfootUrl = "http://112.124.13.85:8080/static/";
- cssUrl = "/asset/style/";
- break;
- case 2:
- bigfootUrl = "http://localhost:8080/bigfoot/";
- break;
- }
- var sitePath = request.getLevelByUrl(require(siteRoot + '/public/struct')),
- first = sitePath[0];
- var htmlResult = ejs.render(tpl, {
- filename : tplPath,
- bigfootUrl: bigfootUrl,
- cssUrl : cssUrl,
- projectState: projectState,
- query_request: request.toJSON(),
- request: request,
- config: require(siteRoot + '/public/config'),
- struct: require(siteRoot + '/public/struct'),
- sitePath : sitePath,
- firstLevel : first
- });
- // _c.log(first.children.length)
- response.end200(htmlResult);
- });
- }else{
- connect.static(siteRoot)(request, response, function(){
- // if not found...
- response.writeHead(404, {'Content-Type': 'text/html'});
- response.end('404');
- });
- }
- }