Mustache.js/Hogan.js 模板预编译

mustache.js粉笔网用的一个开源前端模板引擎,无逻辑的设计,简单好用,性能也不错。

一个简单的 mustache.js 渲染例子 demo.js
1
2
3
var template = "hello {name}}!"; // 因为代码高亮插件的 bug,这里 name 左边少了一个 {,实际代码中要加上
console.log(Mustache.render(template, {name: "foo"})); // hello foo!
console.log(Mustache.render(template, {name: "bar"})); // hello bar!

Mustache 在 render 一个模板时,首先会将这个模板编译成一个模板函数。比如上面例子里的 hello {{name}} 模板,会被编译成一个模板函数:

1
2
3
function anonymous(c, r) {
    return "" + "hello\u0020" + r._name("name", c, true) + "\u0021";
}

大规模应用时,模板的编译过程会花掉整个 render 过程中 30% 左右的时间。

使用 Hogan.js 预编译 Mustache 模板

Mustache 模板的这个问题已经被不少人遇到,也有很多解决办法。比如 twitter 发布的 Hogan.js。Hogan.js 是 Mustache 模板引擎的另一套实现,增加了预编译机制,使得模板字符串可以在打包阶段被预先处理成模板函数,这样浏览器就不必再重复去编译模板。

Hogan.js 同时提供了可以运行与浏览器端和 node.js 环境下的代码,node.js 负责打包时预编译,浏览器端负责用预编译后的代码渲染页面。

首先通过 npm 安装 hogan.js 的 node.js 环境:

1
$ npm install hogan.js

然后对源代码进行一些修改,在模板字符串的旁边加上一些标记,让打包脚本可以找到模板字符串:

修改后的例子 demo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var Template = {
    _cache: {},

    // 所有的模板放在这个对象下
    _template: {
        hello: /*TMPL*/"hello {name}}!"/*TMPL*/ // 因为代码高亮插件的 bug,这里 name 左边少了一个 {,实际代码中要加上
    },

    // 这个适配函数会同时处理字符串模板和模板函数的情况
    render: function (name, data) {
        if (!this._cache[name]) {
            // 如果代码被预编译过,则不需要 compile
            if (typeof this._template[name] === 'function') {
                this._cache[name] = new Hogan.Template(this._template[name]);
            } else if (typeof this._template[name] === 'string') {
                this._cache[name] = Hogan.compile(this._template[name]);
            }
        }

        return this._cache[name].render(data);
    }
};

console.log(Template.render('hello', {name: "foo"})); // hello foo!
console.log(Template.render('hello', {name: "bar"})); // hello bar!
nodejs 环境中的预编译过程
1
2
3
4
5
6
7
8
9
var hogan = require("hogan.js");
var fs = require("fs");
var fileContent = fs.readFileSync("demo.js", "utf-8");
fileContent.replace(/\/\*TMPL\*\/"(.*?)"\/\*TMPL\*\//g, function ($0, $1) {
    return hogan.compile($1, {
        asString: true
    });
});
fs.writeFileSync("demo.js", fileContent, "utf-8");

源代码编译完之后,模板字符串就变成了模板函数:

1
2
3
4
5
6
/* ... */
    hello: function(c,p,i){var _=this;_.b(i=i||"");_.b("hello ");_.b(_.v(_.f("name",c,p,0)));_.b("!");return _.fl();;}
/* ... */

console.log(Template.render('hello', {name: "foo"})); // hello foo!
console.log(Template.render('hello', {name: "bar"})); // hello bar!

参考资料

- FIN -

粉笔网招聘开发工程师

粉笔网是个学习社区,网易前高管离职创业。8 月 27 日正式上线。目前的产品形态是微博形式的,推出以后老师和同学普遍反映上手容易,也希望能加入更多方便学习交流的功能。

目前我们的技术开发团队 7 个人,涵盖数据库、Web 开发、Android、iOS 等多个方面,团队整体实力平均,同时希望各个方面都能再有 1-2 个新同学参与。

我在团队中主要负责前端这块,目前我们的前端 JS 框架是 SeaJS + Backbone 这样的组合,CSS 框架则是 Less。同时还应用了 jQuery/underscore/hogan 这些目前比较流行的库做开发。不需要太多考虑 IE6 下的兼容性,如果你喜欢前端这块的新技术,那这里的工作对于你来说会有一种“玩着就把钱挣了”的感觉。

待遇方面,前端研发工程师的待遇和其他研发工程师待遇相同,都是 18w-30w,正规五险一金、转正配一笔性感诱人的期权、餐补报销、免费可乐水果管饱、iMac 双显、帅哥靓妹协助解决单身问题什么都是妥妥的。

公司目前在东三环朝外 SOHO,如果你在五道口这则有同事的顺风车可搭。

我希望未来一起并肩战斗的同学应该是:
  • 有信心扛起一整个网站,而不只是负责一小部分 feature 的开发
  • 解决问题时不仅仅是霰弹枪一把而是愿意研究问题背后的原因
  • Google 资料/上栈爆网/混 Github 毫无压力
  • 有无限的求知欲望和探索欲望,对于不整洁的代码有天然的愤怒感
这样的同学,欢迎你来我们这里:
  • 全职或每周 4 天以上的实习
  • 负责一个超大型网站的 Web 开发
  • 培养起一个小型技术团队
  • Being geek

如果你是这样,或者你希望将来称为这样的工程师,欢迎通过以下方式联系我:

- FIN -