Jade 基于 Node.js 功能丰富的模版引擎

Jade 是一个高性能的模板引擎,它深受 Haml 影响,使用 JavaScript 前端开发脚本实现的,并且可以供 Node 使用,Jade的语法简单易懂,能提供常见的循环遍历引用等语法。

Jade 基于 Node.js 功能丰富的模版引擎

安装

通过 Npm 安装

$ npm install jade

在 Express 中调用 Jade 模板引擎

var express = require('express');

var http = require('http');

var app = express();

app.set('view engine', 'jade'); // 设置模板引擎

app.set('views', __dirname); // 设置模板相对路径(相对当前目录)

app.get('/', function(req, res) {

res.render('test'); // 调用当前路径下的 test.jade 模板

})

var server = http.createServer(app);

server.listen(3002);

console.log('server started on http://127.0.0.1:3002/');

特性

  • 客户端支持
  • 代码高可读
  • 灵活的缩进
  • 块展开
  • Mixins
  • 静态包含
  • 属性改写
  • 安全,默认代码是转义的
  • 运行时和编译时上下文错误报告
  • 命令行下编译jade模板
  • HTML5 模式 (使用 !!! 5 文档类型)
  • 在内存中缓存(可选)
  • 合并动态和静态标签类
  • 可以通过 filters 修改树
  • 模板继承
  • 原生支持 Express JS
  • 通过 each 枚举对象、数组甚至是不能枚举的对象
  • 块注释
  • 没有前缀的标签
  • AST Filters
  • 过滤器

    • :stylus 必须已经安装 stylus
    • :less 必须已经安装 less.js
    • :markdown 必须已经安装 markdown-js 或者 node-discount
    • :cdata
    • :coffeescript 必须已经安装coffee-script

  • Emacs Mode
  • Vim Syntax
  • TextMate Bundle
  • Coda/SubEtha syntax Mode
  • Screencasts
  • html2jade 转换器

简单示例

Jade 是一个干净的简洁的HTML模版引擎,你可以专注的书写你的HTML代码,下面是一个例子:

doctype html

html(lang="en")

head

title= pageTitle

script(type='text/javascript').

if (foo) bar(1 + 5)

body

h1 Jade - node template engine

#container.col

if youAreUsingJade

p You are amazing

else

p Get on it!

p.

Jade is a terse and simple templating language with a

span focus on performance and powerful features.

上面的代码将生成下面的HTML代码

<!DOCTYPE html>

<html lang="en">

<head>

<title>Jade</title>

<script type="text/javascript">

if (foo) bar(1 + 5)

</script>

</head>

<body>

<h1>Jade - node template engine</h1>

<div id="container" class="col">

<p>You are amazing</p>

<p>Jade is a terse and simple templating language with a span focus on performance and powerful features.</p>

</div>

</body>

</html>

Jade官方教程是一个伟大的地方开始。虽然正在完成,您可以查看一些旧的文档。

API

需要查看完整的API文档,请参阅:jade-lang.com/api

var jade = require('jade');

// compile

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));

可选参数

  • self 使用 self 命名空间来持有本地变量. (默认为 false)
  • locals 本地变量对象
  • filename 异常发生时使用,includes 时必需
  • debug 输出 token 和翻译后的函数体
  • compiler 替换掉 jade 默认的编译器
  • compileDebugfalse的时候调试的结构不会被输出
  • pretty 为输出加上了漂亮的空格缩进 (默认为 false)

浏览器支持

把 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('\n<p>Hello ' + escape((interp = name) == null ? '' : interp) + '\n</p>');

}

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('\n<p>Hello ' + escape((interp = name) == null ? '' : interp) + '\n</p>');

}

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('\n<p>Hello ' + escape((interp = name) == null ? '' : interp) + '\n</p>');

}

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默认的编译器
  • compileDebugfalse的时候调试的结构不会被输出

语法

行结束标志

CRLF 和 CR 会在编译之前被转换为 LF

标签

标签就是一个简单的单词:

html

它会被转换为 <html></html>

标签也是可以有id的:

div#container

它会被转换为 <div id="container"></div>

怎么加类呢?

div.user-details

转换为 <div class="user-details"></div>

多个类? 和id? 也是可以搞定的:

div#foo.bar.baz

转换为 <div id="foo" class="bar baz"></div>

不停的div div div 很讨厌啊 , 可以这样:

#foo

.bar

这个算是我们的语法糖,它已经被很好的支持了,上面的会输出:

`<div id="foo"></div><div class="bar"></div>`

标签文本

只需要简单的把内容放在标签之后:

p wahoo!

它会被渲染为 <p>wahoo!</p>.

很帅吧,但是大段的文本怎么办呢:

p

| foo bar baz

| rawr rawr

| super cool

| go jade go

渲染为 <p>foo bar baz rawr.....</p>

怎么和数据结合起来? 所有类型的文本展示都可以和数据结合起来,如果我们把{ name: 'tj', email: 'tj@vision-media.ca' } 传给编译函数,下面是模板上的写法:

#user #{name} &lt;#{email}&gt;

它会被渲染为 <div id="user">tj &lt;tj@vision-media.ca&gt;</div>

当就是要输出#{} 的时候怎么办? 转义一下!

p \#{something}

它会输出<p>#{something}</p>

同样可以使用非转义的变量!{html}, 下面的模板将直接输出一个script标签

- var html = "<script></script>"

| !{html}

内联标签同样可以使用文本块来包含文本:

label

| Username:

input(name='user[name]')

或者直接使用标签文本:

label Username:

input(name='user[name]')

包含文本的标签,比如script, style, 和 textarea 不需要前缀| 字符, 比如:

  html

head

title Example

script

if (foo) {

bar();

} else {

baz();

}

这里还有一种选择,可以使用’.’ 来开始一段文本块,比如:

  p.

foo asdf

asdf

asdfasdfaf

asdf

asd.

会被渲染为:

    <p>foo asdf

asdf

asdfasdfaf

asdf

asd

.

</p>

这和带一个空格的 ‘.’ 是不一样的, 带空格的会被Jade的解析器忽略,当作一个普通的文字:

p .

渲染为:

<p>.</p>

需要注意的是广西块需要两次转义。比如想要输出下面的文本:

</p>foo\bar</p>

使用:

p.

foo\\bar

注释

单行注释和JavaScript里是一样的,通过”//”来开始,并且必须单独一行:

// just some paragraphs

p foo

p bar

渲染为:

<!-- just some paragraphs -->

<p>foo</p>

<p>bar</p>

Jade 同样支持不输出的注释,加一个短横线就行了:

//- will not output within markup

p foo

p bar

渲染为:

<p>foo</p>

<p>bar</p>

块注释

块注释也是支持的:

  body

//

#content

h1 Example

渲染为:

<body>

<!--

<div id="content">

<h1>Example</h1>

</div>

-->

</body>

Jade 同样很好的支持了条件注释:

body

//if IE

a(target="_blank" class="external-link" 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

属性

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

| <h1>Title</h1>

| <p>foo bar baz</p>

或者我们可以使用. 来告诉Jade我们需要一段文本:

html

body.

<h1>Title</h1>

<p>foo bar baz</p>

上面的两个例子都会渲染成相同的结果:

<html><body><h1>Title</h1>

<p>foo bar baz</p>

</body></html>

这条规则适应于在jade里的任何文本:

html

body

h1 User <em>#{name}</em>

Doctypes

添加文档类型只需要简单的使用 !!!, 或者 doctype 跟上下面的可选项:

!!!

会渲染出 transitional 文档类型, 或者:

!!! 5

or

!!! html

or

doctype html

doctypes 是大小写不敏感的, 所以下面两个是一样的:

doctype Basic

doctype basic

当然也是可以直接传递一段文档类型的文本:

doctype html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN

渲染后:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN>

会输出 html 5 文档类型. 下面的默认的文档类型,可以很简单的扩展:

var doctypes = exports.doctypes = {

'5': '<!DOCTYPE html>',

'xml': '<?xml version="1.0" encoding="utf-8" ?>',

'default': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',

'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',

'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',

'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',

'1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',

'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',

'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'

};

通过下面的代码可以很简单的改变默认的文档类型:

jade.doctypes.default = 'whatever you want';

过滤器

过滤器前缀 :, 比如 :markdown 会把下面块里的文本交给专门的函数进行处理。查看顶部 特性 里有哪些可用的过滤器。

body

:markdown

Woah! jade _and_ markdown, very **cool**

we can even link to [stuff](http://google.com)

渲染为:

   <body><p>Woah! jade <em>and</em> markdown, very <span>cool</span> we can even link to <a target="_blank" class="external-link" href="http://google.com">stuff</a></p></body>

代码

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<h1>bar</h1>. 为了安全起见,使用=输出的代码默认是转义的,如果想直接输出不转义的值可以使用!=

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

渲染为:

<li>one</li>

<li>two</li>

<li>three</li>

遍历一个数组同时带上索引:

items = ["one", "two", "three"]

each item, i in items

li #{item}: #{i}

渲染为:

<li>one: 0</li>

<li>two: 1</li>

<li>three: 2</li>

遍历一个数组的键值:

obj = { foo: 'bar' }

each val, key in obj

li #{key}: #{val}

将会渲染为:<li>foo: bar</li>

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 支持通过 blockextends 关键字来实现模板继承。 一个块就是一个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里又定义了两个可以被实现的块sidebarprimary,或者子模板直接实现content

extends regular-layout

block content

.sidebar

block sidebar

p nothing

.primary

block primary

p nothing

包含

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/headincludes/foot 都会读取相对于给 layout.jade 参数filename 的路径的文件, 这是一个绝对路径,不用担心Express帮你搞定这些了。Include 会解析这些文件,并且插入到已经生成的语法树中,然后渲染为你期待的内容:

<html>

<head>

<title>My Site</title>

<script src="/javascripts/jquery.js">

</script><script src="/javascripts/app.js"></script>

</head>

<body>

<h1>My Site</h1>

<p>Welcome to my super lame site.</p>

<div id="footer">

<p>Copyright>(c) foobar</p>

</div>

</body>

</html>

前面已经提到,include 可以包含比如html或者css这样的内容。给定一个扩展名后,Jade不会把这个文件当作一个Jade源代码,并且会把它当作一个普通文本包含进来:

html

body

include content.html

Include 也可以接受块内容,给定的块将会附加到包含文件 最后 的块里。 举个例子,head.jade 包含下面的内容:

head

script(src='/jquery.js')

我们可以像下面给include head添加内容, 这里是添加两个脚本.

html

include head

script(src='/foo.js')

script(src='/bar.js')

body

h1 test

Mixins

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

  mixin list

ul

li foo

li bar

li baz

使用不带参数的mixin看上去非常简单,在一个块外:

  h2 Groceries

mixin list

Mixins 也可以带一个或者多个参数,参数就是普通的javascripts表达式,比如下面的例子:

  mixin pets(pets)

ul.pets

- each pet in pets

li= pet

mixin profile(user)

.user

h2= user.name

mixin pets(user.pets)

会输出像下面的html:

<div class="user">

<h2>tj</h2>

<ul class="pets">

<li>tobi</li>

<li>loki</li>

<li>jane</li>

<li>manny</li>

</ul>

</div>

产生输出

假设我们有下面的Jade源码:

- var title = 'yay'

h1.title #{title}

p Just an example

compileDebug 选项不是false, Jade 会编译时会把函数里加上 __.lineno = n;, 这个参数会在编译出错时传递给rethrow(), 而这个函数会在Jade初始输出时给出一个有用的错误信息。

function anonymous(locals) {

var __ = { lineno: 1, input: "- var title = 'yay'\nh1.title #{title}\np Just an example", filename: "testing/test.js" };

var rethrow = jade.rethrow;

try {

var attrs = jade.attrs, escape = jade.escape;

var buf = [];

with (locals || {}) {

var interp;

__.lineno = 1;

var title = 'yay'

__.lineno = 2;

buf.push('<h1');

buf.push(attrs({ "class": ('title') }));

buf.push('>');

buf.push('' + escape((interp = title) == null ? '' : interp) + '');

buf.push('</h1>');

__.lineno = 3;

buf.push('<p>');

buf.push('Just an example');

buf.push('</p>');

}

return buf.join("");

} catch (err) {

rethrow(err, __.input, __.filename, __.lineno);

}

}

compileDebug 参数是false, 这个参数会被去掉,这样对于轻量级的浏览器端模板是非常有用的。结合Jade的参数和当前源码库里的 ./runtime.js 文件,你可以通过toString()来编译模板而不需要在浏览器端运行整个Jade库,这样可以提高性能,也可以减少载入的JavaScript数量。

function anonymous(locals) {

var attrs = jade.attrs, escape = jade.escape;

var buf = [];

with (locals || {}) {

var interp;

var title = 'yay'

buf.push('<h1');

buf.push(attrs({ "class": ('title') }));

buf.push('>');

buf.push('' + escape((interp = title) == null ? '' : interp) + '');

buf.push('</h1>');

buf.push('<p>');

buf.push('Just an example');

buf.push('</p>');

}

return buf.join("");

}

Makefile 的一个例子

通过执行make, 下面的Makefile例子可以把 pages/*.jade 编译为 pages/*.html

JADE = $(shell find pages/*.jade)

HTML = $(JADE:.jade=.html)

all: $(HTML)

%.html: %.jade

jade < $< --path $< > $@

clean:

rm -f $(HTML)

.PHONY: clean

这个可以和watch(1) 命令起来产生像下面的行为:

 $ watch make

命令行的jade(1)

使用: jade [options] [dir|file ...]

选项:

-h, --help 输出帮助信息

-v, --version 输出版本号

-o, --obj <str> javascript选项

-O, --out <dir> 输出编译后的html到<dir>

-p, --path <path> 在处理stdio时,查找包含文件时的查找路径

Examples:

# 编译整个目录

$ jade templates

# 生成 {foo,bar}.html

$ jade {foo,bar}.jade

# 在标准IO下使用jade

$ jade < my.jade > my.html

# 在标准IO下使用jade, 同时指定用于查找包含的文件

$ jade < my.jade -p my.jade > my.html

# 在标准IO下使用jade

$ echo "h1 Jade!" | jade

# foo, bar 目录渲染到 /tmp

$ jade foo bar --out /tmp

相关链接

  • Github地址:https://github.com/pugjs/jade
  • 项目地址:http://jade-lang.com/

以上是 Jade 基于 Node.js 功能丰富的模版引擎 的全部内容, 来源链接: utcz.com/p/232281.html

回到顶部