ejs 是常用的模版引擎之一,co-ejs 是经过改造 ejs 源码以支持生成器函数的一种尝试。我们通常所理解的模板引擎是将定义好的模版与静态数据(如JSON)结合渲染生成 HTML ,而有了co-ejs,我们可以做些更有意思的事情,我们来看 co-ejs 的 example :

app.js

var path = require('path');
var app = require('koa')();
var wait = require('co-wait');
var render = require('co-ejs');

var locals = {
  version: '1.0.0',
  now: function () {
    return new Date();
  },
  ip: function *() {
    yield wait(1000);
    return this.ip;
  },
  callback: function() {
    return function (cb) {
      cb(null, '<p>callback</p>');
    }
  },
  callbackGen: function() {
    return function* () {
      yield wait(3000);
      return '<p>callbackGen</p>';
    };
  },
  doNothing: function() {
    console.log('this will not print');
  }
};

var filters = {
  format: function (time) {
    return time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate();
  }
};

app.use(render(app, {
  root: path.join(__dirname, 'views'),
  layout: 'layout',
  viewExt: 'html',
  cache: true,
  debug: true,
  locals: locals,
  filters: filters
}));

app.use(function *() {
  console.time('time');
  yield this.render('content', {
    users: [{name: 'John'}, {name: 'Jack'}, {name: 'Tom'}]
  });
  console.timeEnd('time');
});

app.listen(3000, function () {
  console.log('listening on 3000.');
});

content.html

<div>
<p>version: <%= version %></p>
<p>now: <%=: now() | format %></p>
<p>ip: <%= ip %></p>
<% var cb = yield callback() %>
<p>callbackGen: <%= callbackGen() %></p>
<p>callback: <%- cb %></p>
<p><%=: users | map : 'name' | join %></p>
<% include user.ejs %>
</div>

可以看出,在 co-ejs 中,我们不仅可以传入一个普通变量、函数,甚至可以直接在模版中使用生成器函数、生成器以及 yield。 co-ejs 会将以 <%= %> 或 <%- %> 包裹的表达式并行执行,即 ip 函数延时 1s 执行结束,callbackGen 函数延时 3s 结束,最终渲染模版只需 3s。而在 <% %> 内使用 yield 则会顺序执行,假如将上例中的 callback 函数修改为:

callback: function() {
  return function (cb) {
    setTimeout(function () {
      cb(null, '<h1>callback</h1>');
    }, 2000);
  };
}

则渲染模版需要花 5s。