导航:
试玩
特性
其它实现
安装
浏览器支持
API
选项
标签
标签文本
注释
块注释
内联
块展开
Case
属性
HTML
Doctypes
过滤器
代码
循环
条件语句
模板继承
前置、追加代码块
包含
Mixins
产生输出
Makefile 的一个例子
命令行的 Jade
教程
许可
项目主页
Jade 是一个高性能的模板引擎,它深受 Haml 影响,它是用 JavaScript 实现的,并且可以供 Node 使用。
试玩
你可以在网上试玩 Jade。
特性
客户端支持
代码高可读
灵活的缩进
块展开
Mixins
静态包含
属性改写
安全,默认代码是转义的
运行时和编译时上下文错误报告
命令行下编译jade模板
HTML5 模式 (使用
!!! 5
文档类型)在内存中缓存(可选)
合并动态和静态标签类
可以通过
filters
修改树模板继承
原生支持 Express JS
通过
each
枚举对象、数组甚至是不能枚举的对象块注释
没有前缀的标签
AST Filters
过滤器
:stylus
必须已经安装 stylus:less
必须已经安装 less.js:markdown
必须已经安装 markdown-js 或者 node-discount:cdata
:coffeescript
必须已经安装coffee-scriptEmacs Mode
Vim Syntax
TextMate Bundle
Coda/SubEtha syntax Mode
Screencasts
html2jade 转换器
其它实现
jade有其他语言的实现,可以实现前后端渲染的统一:
php
scala
ruby
python
java
安装
npm install jade
浏览器支持
把 Jade 编译为一个可供浏览器使用的单文件,只需要简单的执行:
make jade.js
如果你已经安装了 uglifyjs (npm install uglify-js
),你可以执行下面的命令它会生成所有的文件。其实每一个正式版本里都帮你做了这事。
make jade.min.js
默认情况下,为了方便调试Jade会把模板组织成带有形如 __.lineno = 3
的行号的形式。 在浏览器里使用的时候,你可以通过传递一个选项 { compileDebug: false }
来去掉这个。
下面的模板
p Hello #{name}
会被翻译成下面的函数:
function anonymous(locals, attrs, escape, rethrow) { var buf = []; with (locals || {}) { var interp; buf.push('nHello ' + escape((interp = name) == null ? '' : interp) + 'n
'); } return buf.join(""); }
通过使用 Jade 的 ./runtime.js
你可以在浏览器使用这些预编译的模板而不需要使用 Jade, 你只需要使用 runtime.js
里的工具函数, 它们会放在 jade.attrs
, jade.escape
这些里。 把选项 { client: true }
传递给 jade.compile()
, Jade 会把这些帮助函数的引用放在jade.attrs
, jade.escape
.
function anonymous(locals, attrs, escape, rethrow) { var attrs = jade.attrs, escape = jade.escape, rethrow = jade.rethrow; var buf = []; with (locals || {}) { var interp; buf.push('nHello ' + escape((interp = name) == null ? '' : interp) + 'n
'); } return buf.join(""); }
API
var jade = require('jade'); // Compile a function var fn = jade.compile('string of jade', options); fn(locals);
选项
self
使用self
命名空间来持有本地变量. (默认为false
)locals
本地变量对象filename
异常发生时使用,includes 时必需debug
输出 token 和翻译后的函数体compiler
替换掉 jade 默认的编译器compileDebug
false
的时候调试的结构不会被输出pretty
为输出加上了漂亮的空格缩进 (默认为false
)
标签
标签就是一个简单的单词:
html
它会被转换为
标签也是可以有 id 的:
div#container
它会被转换为
怎么加 class 呢?
div.user-details
转换为
多个 class 和 id? 也是可以搞定的:
div#foo.bar.baz
转换为
不停的 div div div
很讨厌啊 , 可以这样:
#foo .bar
这个算是我们的语法糖,它已经被很好的支持了,上面的会输出:
标签文本
只需要简单的把内容放在标签之后:
p wahoo!
它会被渲染为 wahoo!
.
很帅吧,但是大段的文本怎么办呢:
p | foo bar baz | rawr rawr | super cool | go jade go
渲染为 foo bar baz rawr.....
怎么和数据结合起来? 所有类型的文本展示都可以和数据结合起来,如果我们把 { name: 'tj', email: 'tj@vision-media.ca' }
传给编译函数,下面是模板上的写法:
#user #{name} <#{email}>
它会被渲染为
当就是要输出 #{}
的时候怎么办? 转义一下!
p #{something}
它会输出 #{something}
同样可以使用非转义的变量 !{html}
, 下面的模板将直接输出一个 标签:
- var html = "" | !{html}
内联标签同样可以使用文本块来包含文本:
label | Username: input(name='user[name]')
或者直接使用标签文本:
label Username: input(name='user[name]')
只 包含文本的标签,比如 ,
, 和
不需要前缀
|
字符, 比如:
html head title Example script if (foo) { bar(); } else { baz(); }
这里还有一种选择,可以使用 .
来开始一段文本块,比如:
p. foo asdf asdf asdfasdfaf asdf asd.
会被渲染为:
foo asdf asdf asdfasdfaf asdf asd .
这和带一个空格的 .
是不一样的, 带空格的会被 Jade 的解析器忽略,当作一个普通的文字:
p .
渲染为:
.
需要注意的是文本块需要两次转义。比如想要输出下面的文本:
foobar
使用:
p. foo\bar
注释
单行注释和 JavaScript 里是一样的,通过 //
来开始,并且必须单独一行:
// just some paragraphs p foo p bar
渲染为:
foo
bar
Jade 同样支持不输出的注释,加一个短横线就行了:
//- will not output within markup p foo p bar
渲染为:
foo
bar
块注释
块注释也是支持的:
body // #content h1 Example
渲染为:
Jade 同样很好的支持了条件注释:
body //if IE a(href='http://www.mozilla.com/en-US/firefox/') Get Firefox
渲染为:
内联
Jade 支持以自然的方式定义标签嵌套:
ul li.first a(href='#') foo li a(href='#') bar li.last a(href='#') baz
块展开
块展开可以帮助你在一行内创建嵌套的标签,下面的例子和上面的是一样的:
ul li.first: a(href='#') foo li: a(href='#') bar li.last: a(href='#') baz
Case
case
表达式按下面这样的形式写:
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
属性
Jade 现在支持使用 (
和 )
作为属性分隔符
a(href='/login', title='View login page') Login
当一个值是 undefined
或者 null
属性 不 会被加上,
所以呢,它不会编译出 'something="null"'.
div(something=null)
Boolean 属性也是支持的:
input(type="checkbox", checked)
使用代码的 Boolean 属性只有当属性为 true
时才会输出:
input(type="checkbox", checked=someValue)
多行同样也是可用的:
input(type='checkbox', name='agreement', checked)
多行的时候可以不加逗号:
input(type='checkbox' name='agreement' checked)
加点空格,格式好看一点?同样支持
input( type='checkbox' name='agreement' checked)
冒号也是支持的:
rss(xmlns:atom="atom")
假如我有一个 user
对象 { id: 12, name: 'tobi' }
我们希望创建一个指向 /user/12
的链接 href
, 我们可以使用普通的 JavaScript 字符串连接,如下:
a(href='/user/' + user.id)= user.name
或者我们使用 Jade 的修改方式, 这个我想很多使用 Ruby 或者 CoffeeScript 的人会看起来像普通的 JS..:
a(href='/user/#{user.id}')= user.name
class
属性是一个特殊的属性,你可以直接传递一个数组,比如 bodyClasses = ['user', 'authenticated']
:
body(class=bodyClasses)
HTML
内联的 HTML 是可以的,我们可以使用管道定义一段文本 :
html body |Title
|foo bar baz
或者我们可以使用 .
来告诉 Jade 我们需要一段文本:
html body.Title
foo bar baz
上面的两个例子都会渲染成相同的结果:
Title
foo bar baz
这条规则适应于在 Jade 里的任何文本:
html body h1 User #{name}
Doctypes
添加文档类型只需要简单的使用 !!!
, 或者 doctype
跟上下面的可选项:
!!!
会渲染出 transitional 文档类型, 或者:
!!! 5
或
!!! html
或
doctype html
Doctype 是大小写不敏感的, 所以下面两个是一样的:
doctype Basic doctype basic
当然也是可以直接传递一段文档类型的文本:
doctype html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"
渲染后:
会输出 HTML5 文档类型. 下面的默认的文档类型,可以很简单的扩展:
var doctypes = exports.doctypes = { '5': '', 'xml': '', 'default': '', 'transitional': '', 'strict': '', 'frameset': '', '1.1': '', 'basic': '', 'mobile': '' };
通过下面的代码可以很简单的改变默认的文档类型:
jade.doctypes.default = 'whatever you want';
过滤器
过滤器前缀 :
, 比如 :markdown
会把下面块里的文本交给专门的函数进行处理。查看顶部 特性 里有哪些可用的过滤器。
body :markdown Woah! jade _and_ markdown, very **cool** we can even link to [stuff](http://google.com)
渲染为:
Woah! jade and markdown, very cool we can even link to stuff
代码
Jade 目前支持三种类型的可执行代码。第一种是前缀 -
, 这是不会被输出的:
- var foo = 'bar';
这可以用在条件语句或者循环中:
- for (var key in obj) p= obj[key]
由于 Jade 的缓存技术,下面的代码也是可以的:
- if (foo) ul li yay li foo li worked - else p oh no! didnt work
哈哈,甚至是很长的循环也是可以的:
- if (items.length) ul - items.forEach(function(item){ li= item - })
所以你想要的!
下一步我们要 转义 输出的代码,比如我们返回一个值,只要前缀一个 =
:
- var foo = 'bar' = foo h1= foo
它会渲染为 bar
. 为了安全起见,使用 bar
=
输出的代码默认是转义的,如果想直接输出不转义的值可以使用 !=
:
p!= aVarContainingMoreHTML
Jade 同样是设计师友好的,它可以使 JavaScript 更直接更富表现力。比如下面的赋值语句是相等的,同时表达式还是通常的 JavaScript:
- var foo = 'foo ' + 'bar' foo = 'foo ' + 'bar'
Jade 会把 if
, else if
, else
, until
, while
, unless
同别的优先对待, 但是你得记住它们还是普通的 JavaScript:
if foo == 'bar' ul li yay li foo li worked else p oh no! didnt work
循环
尽管已经支持 JavaScript 原生代码,Jade 还是支持了一些特殊的标签,它们可以让模板更加易于理解,其中之一就是 each
, 这种形式:
each VAL[, KEY] in OBJ
一个遍历数组的例子 :
- var items = ["one", "two", "three"] each item in items li= item
渲染为:
遍历一个数组同时带上索引:
items = ["one", "two", "three"] each item, i in items li #{item}: #{i}
渲染为:
遍历一个数组的键值:
obj = { foo: 'bar' } each val, key in obj li #{key}: #{val}
将会渲染为:
Jade 在内部会把这些语句转换成原生的 JavaScript 语句,就像使用 users.forEach(function(user){
, 词法作用域和嵌套会像在普通的 JavaScript 中一样:
each user in users each role in user.roles li= role
如果你喜欢,也可以使用 for
:
for user in users for role in user.roles li= role
条件语句
Jade 条件语句和使用了(-
) 前缀的 JavaScript 语句是一致的,然后它允许你不使用圆括号,这样会看上去对设计师更友好一点,
同时要在心里记住这个表达式渲染出的是 常规 JavaScript:
for user in users if user.role == 'admin' p #{user.name} is an admin else p= user.name
和下面的使用了常规 JavaScript 的代码是相等的:
for user in users - if (user.role == 'admin') p #{user.name} is an admin - else p= user.name
Jade 同时支持 unless
, 这和 if (!(expr))
是等价的:
for user in users unless user.isAnonymous p | Click to view a(href='/users/' + user.id)= user.name
模板继承
Jade 支持通过 block
和 extends
关键字来实现模板继承。 一个块就是一个 Jade 的 block ,它将在子模板中实现,同时是支持递归的。
Jade 块如果没有内容,Jade 会添加默认内容,下面的代码默认会输出 block scripts
, block content
, 和 block foot
.
html head h1 My Site - #{title} block scripts script(src='/jquery.js') body block content block foot #footer p some footer content
现在我们来继承这个布局,简单创建一个新文件,像下面那样直接使用 extends
,给定路径(可以选择带 .jade
扩展名或者不带). 你可以定义一个或者更多的块来覆盖父级块内容, 注意到这里的 foot
块 没有 定义,所以它还会输出父级的 "some footer content"。
extends extend-layout block scripts script(src='/jquery.js') script(src='/pets.js') block content h1= title each pet in pets include pet
同样可以在一个子块里添加块,就像下面实现的块 content
里又定义了两个可以被实现的块 sidebar
和 primary
,或者子模板直接实现 content
。
extends regular-layout block content .sidebar block sidebar p nothing .primary block primary p nothing
前置、追加代码块
Jade允许你 替换 (默认)、 前置 和 追加 blocks. 比如,假设你希望在 所有 页面的头部都加上默认的脚本,你可以这么做:
html head block head script(src='/vendor/jquery.js') script(src='/vendor/caustic.js') body block content
现在假设你有一个Javascript游戏的页面,你希望在默认的脚本之外添加一些游戏相关的脚本,你可以直接append
上代码块:
extends layout block append head script(src='/vendor/three.js') script(src='/game.js')
使用 block append
或 block prepend
时 block
是可选的:
extends layout append head script(src='/vendor/three.js') script(src='/game.js')
包含
Includes 允许你静态包含一段 Jade, 或者别的存放在单个文件中的东西比如 CSS, HTML 非常常见的例子是包含头部和页脚。 假设我们有一个下面目录结构的文件夹:
./layout.jade ./includes/ ./head.jade ./tail.jade
下面是 layout.jade
的内容:
html include includes/head body h1 My Site p Welcome to my super amazing site. include includes/foot
这两个包含 includes/head
和 includes/foot
都会读取相对于给 layout.jade
参数filename
的路径的文件, 这是一个绝对路径,不用担心Express帮你搞定这些了。Include 会解析这些文件,并且插入到已经生成的语法树中,然后渲染为你期待的内容:
My Site My Site
Welcome to my super lame site.
Copyright>(c) foobar
前面已经提到,include
可以包含比如 HTML 或者 CSS 这样的内容。给定一个扩展名后,Jade 不会把这个文件当作一个 Jade 源代码,并且会把它当作一个普通文本包含进来:
html head //- css and js have simple filters that wrap them in