Pug(原名 Jade)是一款深受开发者喜爱的模板引擎,它以简洁的缩进语法代替繁琐的 HTML 标签,让视图代码更加清晰易写。Pug 功能强大,支持模板继承、包含、混入等高级特性,并与 Express 无缝集成。本章将带你深入掌握 Pug 的用法。
Pug 是一个受 Haml 影响的模板引擎,使用缩进表示 HTML 标签的层级关系,摒弃了尖括号,极大地简化了 HTML 的编写。它最初名为 Jade,后因商标问题更名为 Pug。Pug 的特点包括:
extends 和 block 实现布局复用。在 Express 项目中使用 Pug 非常简单。首先安装 pug 包:
npm install pug
然后在主应用文件中设置模板引擎:
const express = require('express');
const path = require('path');
const app = express();
// 设置模板引擎为 Pug
app.set('view engine', 'pug');
app.set('views', path.join(__dirname, 'views'));
// 静态文件服务(可选)
app.use(express.static(path.join(__dirname, 'public')));
现在,你可以在 views 目录下创建 .pug 文件,并通过 res.render() 渲染它们。
Pug 的核心思想是用缩进代替标签嵌套。下面我们从最简单的例子开始。
在 Pug 中,一行开头的文本默认被解析为 HTML 标签。标签后的空格后的文本即为标签内容。
h1 欢迎使用 Pug
p 这是一个段落。
生成 HTML:
<h1>欢迎使用 Pug</h1>
<p>这是一个段落。</p>
属性放在标签后的括号内,格式为 属性=值,多个属性用逗号分隔。
a(href="https://example.com", target="_blank") 链接
img(src="/images/logo.png", alt="Logo")
生成:
<a href="https://example.com" target="_blank">链接</a>
<img src="/images/logo.png" alt="Logo">
可以使用 CSS 选择器语法快速添加类和 ID。
div.container
#main.content
p.lead 主要内容
生成:
<div class="container">
<div id="main" class="content">
<p class="lead">主要内容</p>
</div>
</div>
如果文本内容较多,可以在标签后加一个点 .,然后缩进写多行文本。
p.
这是第一行。
这是第二行。
这是第三行。
或者使用 | 管道符表示文本行。
p
| 第一行
| 第二行
| 第三行
使用 #{variable} 或 = variable 输出转义后的变量值(防止 XSS)。
- var name = '张三'
p 你好,#{name}!
p= '你好,' + name
如果需要输出原始 HTML(不转义),使用 !{variable} 或 != variable。
- var html = '<strong>加粗</strong>'
p!{html}
Pug 支持两种注释:
// 单行注释:会输出到 HTML。//- 不输出的注释:仅存在于 Pug 文件中。// 这个注释会出现在 HTML 中
//- 这个注释不会出现在 HTML 中
p 段落
Pug 允许在模板中直接使用 JavaScript 代码,通过以 - 开头的行来执行逻辑。
- var user = { name: '李四' }
if user
p 欢迎回来,#{user.name}!
else
p 请登录。
也可以使用 unless(相当于 if (!...))。
使用 each 遍历数组或对象。
ul
each item in ['苹果', '香蕉', '橙子']
li= item
// 遍历对象
dl
each value, key in {name: '张三', age: 30}
dt= key
dd= value
还可以使用 while 循环。
Pug 通过 extends 和 block 实现模板继承,非常适合定义页面布局。
创建布局文件 views/layout.pug:
doctype html
html
head
title #{title}
link(rel="stylesheet", href="/css/style.css")
body
header
h1 我的网站
include partials/nav
main
block content
footer
p 版权所有 © 2025
创建导航部分 views/partials/nav.pug:
nav
a(href="/") 首页
a(href="/about") 关于
a(href="/contact") 联系
在具体页面中使用布局: views/index.pug
extends layout
block content
h2 欢迎访问首页
p 这是使用 Pug 布局生成的页面。
在路由中渲染:
app.get('/', (req, res) => {
res.render('index', { title: '首页' });
});
include 用于将其他 Pug 文件内容嵌入到当前文件中。上例中已经演示了包含导航。包含的文件可以是 Pug、纯文本或 CSS/JS 等。
// 包含 Pug 文件
include partials/footer
// 包含纯文本文件
include:markdown-it README.md
// 包含 CSS 文件(作为内联样式)
style
include style.css
混入允许定义可复用的代码块,类似函数。
// 定义混入
mixin list(items)
ul
each item in items
li= item
// 使用混入
+list(['苹果', '香蕉', '橙子'])
混入还可以接受选项块:
mixin article(title)
.article
.article-header
h2= title
if block
block
.article-content
p 这是文章内容。
+article('标题')
p 这是自定义的头部描述。
在 Express 中渲染 Pug 模板时,可以通过 res.render() 的第二个参数传递数据。
app.get('/profile', (req, res) => {
res.render('profile', {
user: { name: '王五', age: 28 },
skills: ['JavaScript', 'Node.js', 'Express']
});
});
在 profile.pug 中使用数据:
extends layout
block content
h2 用户资料
p 姓名:#{user.name}
p 年龄:#{user.age}
h3 技能:
ul
each skill in skills
li= skill
结合上述特性,构建一个简单的博客列表和详情页。
app.js(片段)
const posts = [
{ id: 1, title: '第一篇博客', content: '这是第一篇博客的内容。' },
{ id: 2, title: '第二篇博客', content: '这是第二篇博客的内容。' }
];
app.get('/', (req, res) => {
res.render('index', { title: '博客首页', posts });
});
app.get('/post/:id', (req, res) => {
const post = posts.find(p => p.id == req.params.id);
if (post) {
res.render('post', { title: post.title, post });
} else {
res.status(404).render('404', { title: '未找到' });
}
});
views/layout.pug
doctype html
html
head
title= title
link(rel="stylesheet", href="/css/style.css")
body
header
h1: a(href="/") 我的博客
nav
a(href="/") 首页
a(href="/about") 关于
main
block content
footer
p © 2025 我的博客
views/index.pug
extends layout
block content
h2 最新文章
if posts.length
each post in posts
article
h3: a(href=`/post/${post.id}`)= post.title
p= post.content.substring(0, 100) + '...'
else
p 暂无文章。
views/post.pug
extends layout
block content
article
h2= post.title
p= post.content
p: a(href="/") 返回首页
views/404.pug
extends layout
block content
h2 404 - 页面未找到
p 您访问的页面不存在。
!{}。