文章目录
  1. 1. Jade
  2. 2. Jade的语法和特性
  3. 3. 语法
    1. 3.1. 标签
    2. 3.2. 属性
    3. 3.3. 注释
    4. 3.4. 读取数据的值
  4. 4. 模板逻辑
    1. 4.1. 条件语句
    2. 4.2. Case
    3. 4.3. 循环
    4. 4.4. 过滤器
    5. 4.5. Mixins
  5. 5. 模板继承
    1. 5.1. 模板继承
    2. 5.2. 包含
  6. 6. 模板特性
    1. 6.1. 标签文本
    2. 6.2. 内联的HTML
    3. 6.3. 嵌套
    4. 6.4. 可执行代码
  7. 7. 其他
    1. 7.1. Doctype
    2. 7.2. 产生输出
  8. 8. 结束语
  9. 9. 在线学习Jade
  10. 10. 参考网站

怎么说呢,心情起伏有点大。兴高采烈地翻译了2个小时40分钟,把关于 Jade 的语法和特性都过了一遍。但是完事之后发现,对它的态度更多的是怪异。

第一,有些操作符和它的真实意思对应不那么直观
比如 !=(非转义输出) 分明是不等于好么;|(原样输出的管道)初识的时候不易接受。

第二,空格缩进过于灵活了吧
一缩进就代表嵌套了,这太吓人了,我就多打了两空格,就从兄弟变成了儿子了;少打两空格,就从儿子变兄弟了。这太容易坑爹了吧。个人觉得,缩进嵌套这本身就挺诡异的,前者是风格,后者是结构,真心觉得没必要绑在一起。比如在 if-else 语句里就容易写错缩进。

第三,是少敲键盘了,但失去了 HTML 的原貌
标签就标签嘛,干嘛要脱掉<>的外衣呢。本来打开一个网页,F12 就能看到现有页面的源码结构,但是当要用 Jade 写时,反而还得再要折腾地变个形。为了所谓的简洁性,代码看起来却更不易读了。

第四,对于 class 和 id 的书写,是更瘦身了,但总觉得不妥
这种借鉴css语法的形式,熟悉css语法的人读起来是so easy了。但对一个外人来说,可读性不好了。原本就像读一句话的id=”container” class=”wrap” 结果变成了符号文 #container.wrap。

在看文档的时候,里面有描述“Jade中的某种写法,同样是设计师友好的”。就比较好奇,为什么要对设计师友好?感觉取悦了狭义上的前端工程师就够了。产品是要只为少数人设计,语言也要一样。

当然,它的优势(个人认为是优势的)也有,比如变量默认是转义的、当属性值是 undefined 或 null 时会自动忽略、模板中可以直接使用 javascript。这些针对 HTML、针对前端的特性做的优化还是很不错的。

之所以选择翻译“模板引擎”这章,主要考虑到:
1、我们正在用 smarty,觉得模板很方便。所以想看看其他兄弟模板是怎么个回事,进而借此机会探究下模板。
2、虚荣心作怪吧,就想探探那高大上的名词“模板”“引擎”。但是今天看到一个大牛这么说“模板技术并不是什么神秘的技术,它干的实际上就是拼接字符串这样很底层的活,只是各种模板有着各自的优缺点和技巧。” 想想人家说的也挺中肯的,模板不就是套数据+加逻辑,最后输出 HTML 字符串嘛。

为了不让今天的付出感觉上白费掉,便决定写个博客记录下目前对它的理解,好提醒下自己:我今天确实是接触到了“新”东西。

Jade

Jade — Node Template Engine,一个高性能的模板引擎,为 Node 而做,用 JavaScript 实现。
Node.js 的 Express.js 框架默认用的就是 Jade。
Jade 还有其他语言的实现,可以实现前后端渲染的统一。比如 php、ruby、python、java、scala。

  1. 通过npm安装: npm install jade
  2. 把Jade编译成一个可供浏览器使用的单文件,执行:make jade.js。
    可以把 .jade 编译为 .html
  3. 命令行:jade [options] [dir|file…]
  4. API
1
2
3
4
5
6
7
8
9
10
11
var jade = require('jade');
//complie
var fn = jade.compile('string of jade', options);
var html = fn(locals);
//render
var html = jade.render('string of jade', merge(options, locals));
//renderFile
var html = jade.renderFile('filename.jade', merge(options, locals));
  1. 浏览器支持
    浏览器版的 Jade 它只支持最新的浏览器,是一个很大的文件。建议使用时先把 Jade 预编译成 JavaScript,然后再在客户端使用 runtime.js 库。

Jade的语法和特性

Jade 的语法是简洁、对 HTML 代码空格敏感。在这点上它借鉴了 Haml 的很多地方,所以与 Haml 语法挺相近

Haml 是什么?Haml 是简写 HTML 的一种办法。借用 Haml 的作者 Hampton Catlin 的一句话:“Haml 是对传统 XHTML 的生成方式的一种反叛。它既不是纯粹的代码,也不是一种文本处理语言。它是与众不同的,是一种在我看来最为自然的 XHTML 构造方式。” Haml 的网站上也说:“Haml 让你撇开臃肿丑陋的模板,将它们以优雅整洁的代码代替。”

  1. 语法
    (1)标签、属性、注释
    (2)读取数据的值(转义、非转义)
  2. 逻辑
    (1)条件语句(if、unless、case)
    (2)循环(each、for)
    (3)过滤器
    (4)Mixins
  3. 继承和包含
    (1)模板继承
    (2)包含
  4. 特性
    (1)输出文本(3种方式:标签文本 | .)
    (2)内联的 HTML(2种方式:| .)
    (3)嵌套(2种方式:缩进嵌套、块展开)
    (4)可执行代码(3种方式:- = )
  5. 其他
    (1)Doctype
    (2)产生输出

语法

标签

标签就是一个简单的单词

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
html
会被转换成 <html></html>
<!--标签的id-->
div#container
会被转成 <div id="container"></div>
<!--标签的class-->
div.user-details
<!--多个class和id-->
div#foo.bar.baz
<!--不写标签,默认就是div(语法糖)-->
#foo
.bar
<div id="foo"></div>
<div class="bar"></div>

属性

属性分隔符是左右括号 ()
当值是 undefined 或 null时,属性不会被加上(所以它不会编译出 xxx=null)
布尔属性,只有当值是 true 时才会输出。为 false 时就被忽略了

class 属性是一个特殊的属性,可以接受一个数组
bodyClasses = [‘user’, ‘authenticated’] :
body(class=bodyClasses)

1
2
3
4
5
<!--普通的 JavaScript 字符串连接-->
a(href='/user/' + user.id)= user.name
<!--用Jade-->
a(href='/user/#{user.id}')= user.name

注释

1、单行注释 // 不输出的注释,加- 即 //-
2、块注释
3、条件注释

1
2
3
4
5
6
7
8
9
body
//if IE
a()
<body>
<!--[if IE]>
<a href="http://www.mozilla.com/en-US/firefox/">Get Firefox</a>
<![endif]-->
</body>

读取数据的值

1、读取变量的值,用 #{}
输出是默认是转义的。比如取变量name的值 #{name}
当要输出#{},则可以转义,即 p #{name}

2、不想转义变量的值 !{name} 【直接输出标签】

1
2
3
4
- var html = "<script></script>"
| !{html}
<!--直接输出<script> 标签 【都原样了,还非转义?】-->

模板逻辑

条件语句

Jade 的条件语句和使用了前缀 - 的 js 语句是一致的,只是条件语句允许你可以不用括号。
一定要在心里明白:这个表达式渲染出的是常规的 js。是相等的

1
2
3
4
5
6
7
8
9
10
11
12
13
//用条件表达式if
for user in users
if user.role == 'admin'
p #{user.name} is an admin
else
p= user.name
//常规的js代码
for user in users
- if (user.role == 'admin')
p #{user.name} is an admin
- else
p= user.name

Jade同时支持 unless 这和 if(!(expr)) 是等价的

Case

case表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--空格缩进-->
html
body
friends = 10
case friends
when 0
p you have no friends
when 1
p you have a friend
default
p you have #{friends} friends
<!--块展开-->
friends = 5
html
body
case friends
when 0: p you have no friends
when 1: p you have a friend
default: p you have #{friends} friends

此处有木有发现,关键字 case、when、default 就和标签傻傻分不清楚了。
有人说可以靠高亮。但是,单词的语义应该是自描述的,你看,一加<>很明显就是标签了嘛。

循环

Jade 支持 js 的原生代码,还支持了一些特殊的标签,让模板更易理解。其中之一就是 each。
each val,key in obj
each item,i in arr

Jade 在内部会把这些语句转换成原生的 js 语句,即 users.foreach(function(user){})
词法作用域和嵌套和在普通的js中一样。
也可以用 for。

过滤器

过滤器前缀:
比如 :markdown 会把下面块的文本交给专门的函数进行处理
可用的过滤器都有
:stylus 必须安装 stylus
:less 必须安装 less.js
:marddown 必须安装 markdown-js 或者 node-discount
:cdata
:coffeescript 必须安装 coffee-script

Mixins

Mixins 在编译的模板里会被 Jade 转换为普通的 js 函数。
Mixins 可以带参,但不是必需的。

模板继承

模板继承

支持通过 block 和 extends 关键字来实现模板继承(继承布局)。
Jade 的 block 就是一个块,它将在子模板中实现。同时支持递归的。
继承的话,extends parent-layout

Jade允许你替换、前置和追加blocks。

block 默认是覆盖(替换)父级块的内容(也可以在子块中实现块)
block append 追加代码块(block可选)
block prepend 前置代码块(block可选)

包含

includes 允许你静态包含一段 Jade,或其他的普通文本(头部、页脚的CSS、HTML)
Jade 解析这些文件,并插入到已经生成的语法树中

被包含的块,可以用 yield 语句(明确标明 include 代码块的放置位置。)

子可以用父中的变量。被包含的 Jade 会按字面解析合并到 AST 中,词法范围的变量的效果和直接在一个文件里写时一样。这就意味着 include 可以用做 partial 的替代。

模板特性

标签文本

1、单行的文本,用标签文本

1
2
3
<!--单行的文本只需要把文本放在标签之后即可,即标签文本-->
p hello
输出 <p>hello</p>

2、文本块 用 |

1
2
3
4
5
6
7
<!--大段的文本,原样输出-->
p
| aaa
| bbb
| ccc
输出 <p>aaa bbb ccc</p>

3、用. 来开始一段文本块
p.
aaa
bbb
ccc.

带空格的 . 会被 Jade 的解析器忽略,当做一个普通的文字。
所以要输出文本块 就不要带空格了,直接.

1
2
3
4
5
<!--注意:文本块需要两次转义-->
要输出下面的文本:</p>foo\bar</p>
需使用:
p.
foo\\bar

eg1.内联标签可以使用标签文本、也可以使用文本块

1
2
3
4
5
6
<!--使用标签文本-->
label
| Username:
<!--直接使用标签文本-->
label Username

eg2.只包含文本的标签,比如 script style textarea,想要输出其中间的内容,不需要使用前缀字符 |

1
2
3
4
5
6
7
8
9
html
head
title Example
script
if (foo) {
bar();
} else {
baz();
}

内联的HTML

想要输出HTML代码片段,可以有以下两种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--1 使用管道定义一段文本-->
html
body
| <h1>Title</h1>
| <p>foo bar baz</p>
<!--2 使用. 来告诉Jade 我们需要一段文本-->
html
body.
<h1>Title</h1>
<p>foo bar baz</p>
<!--以上两个例子都渲染出相同的结果-->
<html>
<body>
<h1>Title</h1>
<p>foo bar baz</p>
</body>
</html>

嵌套

1、空格缩进 以自然的方式定义标签嵌套

1
2
3
4
5
6
7
ul
li.first
a(href='#') foo
li
a(href='#') bar
li.last
a(href='#') baz

2、块展开,在一行内创建嵌套标签,用符号:

1
2
3
4
ul
li.first: a(href='#') foo
li: a(href='#') bar
li.last: a(href='#') baz

可执行代码

Jade目前支持三种类型的可执行代码

第一种前缀 -
这是不会被输出的
可用在条件语句或循环中

1
2
3
4
5
6
7
8
9
- for (var key in obj)
p= obj[key]
-if (foo)
ul
li aaa
li bbb
-else
p hehe

第二种要转义输出的值,用前缀=
我们返回一个值 可以加前缀=,输出的代码默认是转义的
若想不被转义,可使用 !=

1
2
3
4
5
6
7
8
9
10
11
- var foo = 'bar'
= foo
h1= foo
//会被渲染成
bar
<h1>bar</h1>
//以下两行是等价的
- var foo = 'foo ' + 'bar'
foo = 'foo ' + 'bar'

其他

Doctype

添加文档类型
!!!
!!! 5
!!! html
doctype html

产生输出

当选项 compileDebug 的值是 true,Jade 编译时会加行号,且报错信息里会带上行号。
当值是 false 时,参数会被去掉。

你可以通过 toString() 来编译模板而不需要在浏览器端运行整个 Jade 库,这样可以提高性能,也可以减少载入的 JavaScript 数量。

结束语

模板是什么?模板就是一个模子。供你套数据,并且依据不同数据去走不同的逻辑。
引擎是什么?引擎就是个处理器(编译、运行),最后输出个结果(HTML代码)的东西。

好奇心固然重要,但不要被自我无根据的很高预期暗伤到(期望越大失望越大么)。是否会随着翻译的深入,对Jade 的态度有所改观呢?自己留个悬念吧,走着瞧呗。

最后查了下,还真是,关于 Jade 的讨论挺多,褒贬不一。以下的两处讨论都挺激烈,也很严肃。
关于nodejs的模板引擎,如何选择 EJS 和 Jade — 来自知乎
Node.js的模板Jade和EJS哪个好? — 来自stackoverflow
你要是也有兴趣,可加入 谷歌组 去讨论

在线学习Jade

【1】 学习Jade模板引擎的语法,包括:
HTML的标签文本、注释
id、类、属性
变量、转义和非转义

【2】Jade的逻辑处理部分,包括:
在模板中使用内嵌js(node)代码
条件语句(if和unless)
循环(loop、for、each)
定义函数(Mixins)
过滤器(Filters)

【3】在线测试Jade

参考网站

Jade的官方教程
Jade的完整文档
Jade的Git
Jade-模板引擎

其他
Jade-源于 Node.js 的 HTML 模板引擎
nodejs学习13:express3之jade模板引擎入门
Node学习笔记-Jade模板引擎

文章目录
  1. 1. Jade
  2. 2. Jade的语法和特性
  3. 3. 语法
    1. 3.1. 标签
    2. 3.2. 属性
    3. 3.3. 注释
    4. 3.4. 读取数据的值
  4. 4. 模板逻辑
    1. 4.1. 条件语句
    2. 4.2. Case
    3. 4.3. 循环
    4. 4.4. 过滤器
    5. 4.5. Mixins
  5. 5. 模板继承
    1. 5.1. 模板继承
    2. 5.2. 包含
  6. 6. 模板特性
    1. 6.1. 标签文本
    2. 6.2. 内联的HTML
    3. 6.3. 嵌套
    4. 6.4. 可执行代码
  7. 7. 其他
    1. 7.1. Doctype
    2. 7.2. 产生输出
  8. 8. 结束语
  9. 9. 在线学习Jade
  10. 10. 参考网站