前言

我已经很久没有写前端代码了,所以我对于前端js的概念还停留在2007年左右的时代,那时候还没有很好用的前端js模板引擎,所以写漂亮的前端ajax真的很难。数据你可以通过ajax获得到,但是如何漂亮地维护模板呢,当时是没有办法的,所以我当年做小型站点的做法都是ajax直接返回渲染好的HTML代码。现在不用了,jade很好地解决了这个问题。

官方网站:http://jade-lang.com/,奇葩的是,你会发现这个网站就是个静态页面,没有任何内容,它的链接是直接指向jade的github项目网页的,而所有的文档也都写在这里:https://github.com/visionmedia/jade。然而,这个时候你会发现这个文档极其不好用,我是找了很久,才找到一个不错的tutorial网站:http://naltatis.github.com/jade-syntax-docs/#basics,建议需要看例子的,就直接去这个网站好了。

语法

当你真的开始看jade的时候,你会发现,这个模板引擎还不是直接使用HTML的,它自己做了一个语法,来简化HTML代码的编写。这个语法你可以在官方github网页上的syntax部分找到,在我上面给的tutorial里也能找到很多例子。用了一阵你就会发现,这个简化的语法真的很好用,习惯以后HTML的编写速度大大提高。

安装

这里要分成两部分说,一部分是前端js使用的时候,另一部分是后端nodejs使用的时候。

使用

同样需要分为前端和后端。

前端稍微复杂点,需要将模板文件导入到jade中形成render函数,然后给予这个render函数渲染参数就可以了。因为引擎渲染需要的模板资源不是本地磁盘文件,需要进行资源管理,所以有点麻烦。我是这么做的:在seajs中配置好模板文件的名字,然后使用require将模板文件作为string加载下来,最后使用jade渲染:

[codesyntax lang="javascript"]

// client.js
seajs.config({
    base: './',
    alias: {
        ...
        // I18N
        'i18n.rooms':    'js/shared/i18n/rooms.json',
        ...
        // VIEW TEMPLATES
        'view.room':       'views/rooms/room.jade',
        ...
        // VENDORS
        'jade':          'js/lib/jade-0.27.7.min',
        ...
    }
});

// view.js
var jade = require('jade');

proto.renderRoom = function(userId, userName) {
    var render = jade.compile(require('view.room'));
    var params = require('i18n.rooms');
    params['userId'] = userId;
    params['userName'] = userName;
    var html = render(params);
    $('#main').append(html);
};

[/codesyntax]

后端使用的代码就比较简单了,直接将模板文件位置和渲染参数一起给jade的render函数就可以了。这里结合pomelo使用的前端服务器express给出示例代码:

[codesyntax lang="javascript"]

app.configure(function() {
    ...
    app.set('view engine', 'jade');
    app.set('views', __dirname + '/public/views');
    app.set('view options', {layout: false});
    ...
});

app.get('/index.html|/index.htm', function(req, res) {
    var i18nPath = __dirname + '/../shared/i18n/';
    var i18nFrame = require(i18nPath + 'frame/frame.json');
    var i18nIndex = require(i18nPath + 'index.json');
    for (key in i18nIndex) {
        i18nFrame[key] = i18nIndex[key];
    }
    res.render('index.jade', i18nFrame);
    res.end();
});

[/codesyntax]

需要掌握的几个知识点

接下来的内容是我在使用jade的时候遇到的问题已经进行的研究,主要是几个知识点:

  • 几个约定
    • 缩进 - jade的模板里是没有标签闭合的这个概念的,标签只需要写在开始的位置,闭合的渲染由jade负责,因此缩进非常重要,需要严格按照实际生成的HTML编写。e.g

[codesyntax lang="javascript"]

// correct
div
    div
// turn to
<div>
    <div></div>
</div>

// incorrect
div
div

[/codesyntax]

    • 标签内的文本 - 如果要向标签内添加文本,比如<div>TEXTHERE</div>,必须在标签同行写入,否则就会被识别为标签

[codesyntax lang="javascript"]

// correct
div TEXTHERE
// turn to
<div>TEXTHERE</div>

// incorrect
div
    TEXTHERE
// turn to
<div>
    <TEXTHERE></TEXTHERE>
</div>

[/codesyntax]

  • 模板中属性的使用 - 模板中id和class是可以直接写在语法中的,e.g div#divId.classA.classB。其他的属性则需要写在括号中,e.g div(onclick="...")
  • 模板的嵌套和扩展
    • 嵌套 - 使用关键字include

[codesyntax lang="javascript"]

// nav.jade
...
// index.jade
...
    include nav.jade

[/codesyntax]

    • 扩展 - 使用关键字extends和block关键字。需要注意,扩展模板文件中的block关键字后的扩展内容不得和block在一个缩进级别中,否则会报错

[codesyntax lang="javascript"]

// layout.jade
...
    div#main.radius.shadow
        block content

// index.jade
extends frame/layout
block content
    div#login
    ...

[/codesyntax]

  • 参数的使用 - 在模板文件中,使用#{PARAM_NAME}这样的形式来获取

[codesyntax lang="javascript"]

// view.js
proto.renderRoom = function(roomId, roomName) {
    ...
    var html = render({"RID": roomId, "RNAME": roomName});
    ...
};

// room.jade
div.id #{RID}
div.name #{RNAME}

[/codesyntax]

  • 循环的使用

[codesyntax lang="javascript"]

// each VAL[, KEY] in OBJ
// var items = ["one": 1, "two": 2, "three": 3]
each itemVal, itemKey in items
  div
      span itemKey
      span itemVal

[/codesyntax]

Debug

不得不说jade非常容易报错,我在使用express的过程中,不止一次服务器崩掉就是因为jade解析模板错误。这里可以使用 sudo npm install jade -g 的命令来全局安装jade,然后使用 jade /path/to/your/jade/file/file.jade,来进行语法验证。这个操作会产生一个编译后的模板html文件,不需要的话可以删掉。