文章目录
- Node.js Jade(Pug)模板引擎全解析:从简洁语法到视图渲染实践
Node.js Jade(Pug)模板引擎全解析:从简洁语法到视图渲染实践
Jade(后更名为Pug)是Node.js生态中一款高性能、简洁直观的模板引擎,以其缩进式语法和强大的动态渲染能力广受开发者青睐。与传统HTML相比,Jade通过简化标签结构、减少冗余代码,大幅提升了视图层开发效率,尤其适合与Express等Web框架结合构建动态网页。本文系统梳理Jade(Pug)的核心功能、语法规则、实战示例、最佳实践及注意事项,帮助开发者快速掌握这一高效模板引擎的使用。
一、Jade(Pug)模板引擎的核心价值与特点
Jade最初由TJ Holowaychuk开发,后因商标问题更名为Pug,但其核心设计理念和语法保持一致。作为一款“高性能模板引擎”,其核心优势在于:
- 极简语法:采用缩进(空格/制表符)替代HTML的
<>和闭合标签,减少代码冗余(如div.container等价于<div class="container"></div>)。 - 动态渲染:支持变量插入、条件判断、循环迭代、模板继承等逻辑操作,将数据与视图无缝结合。
- 组件化支持:通过“混入(mixin)”实现代码复用,通过“模板继承”实现布局统一,提升代码可维护性。
- 高效编译:将模板编译为原生JavaScript函数,执行效率高于许多同类模板引擎。
- 生态兼容:与Express等Node.js Web框架深度集成,支持自定义过滤器和插件扩展。
适用场景:动态网页渲染(如博客、CMS系统)、邮件模板生成、静态站点生成等需要将数据动态注入视图的场景。
二、Jade(Pug)的安装与基础配置
1. 环境准备
- 依赖Node.js(v12+推荐)和npm/yarn包管理器。
- 需明确:Jade已更名为Pug,当前维护的版本为Pug,安装时需使用
pug包(而非旧版jade)。
2. 安装Pug
# 局部安装(项目依赖)
npm install pug --save
# 与Express结合时(常用场景)
npm install express pug --save
3. 与Express框架集成
在Express中配置Pug作为视图引擎:
// app.js
const express = require('express');
const app = express();
// 配置视图引擎为Pug
app.set('view engine', 'pug');
// 配置视图文件目录(默认是项目根目录下的views文件夹)
app.set('views', './views');
// 定义路由,渲染Pug模板
app.get('/', (req, res) => {
// 向模板传递数据(第二个参数为模板变量)
res.render('index', {
title: 'Pug模板示例',
message: '欢迎使用Pug模板引擎'
});
});
app.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
三、Jade(Pug)核心语法与功能
1. 基础语法:缩进与标签
Pug的核心特点是通过缩进表示层级关系,替代HTML的标签嵌套,语法规则如下:
- 标签书写:直接写标签名(如
div、h1),无需<>。 - 缩进规则:使用空格(推荐2或4个)表示嵌套关系,同一层级缩进必须一致(错误缩进会导致编译失败)。
- 闭合标签:无需手动闭合,Pug会根据缩进自动生成闭合标签。
示例:基础结构
//- views/index.pug(Pug模板文件)
doctype html
html
head
meta(charset="UTF-8")
title= title // 使用=插入变量
body
h1= message
p 这是一个Pug模板示例
div.container
p 这是container容器内的段落
编译后的HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Pug模板示例</title>
</head>
<body>
<h1>欢迎使用Pug模板引擎</h1>
<p>这是一个Pug模板示例</p>
<div class="container">
<p>这是container容器内的段落</p>
</div>
</body>
</html>
2. 属性与样式
- 标签属性:通过
(属性名=值)定义,多个属性用逗号分隔。 - 类与ID:简化写法(
.class对应class属性,#id对应id属性)。 - 样式:支持
style属性对象写法。
示例:属性与样式
//- 属性定义
a(href="https://example.com", target="_blank", title="示例链接") 访问示例网站
//- 类与ID的简化写法
div#main-content.container // 等价于<div id="main-content" class="container">
p.text-primary 这是带类的段落
//- 样式属性
div(style={color: 'red', fontSize: '16px'}) 这是红色文本
编译后:
<a href="https://example.com" target="_blank" title="示例链接">访问示例网站</a>
<div id="main-content" class="container">
<p class="text-primary">这是带类的段落</p>
</div>
<div style="color: red; font-size: 16px;">这是红色文本</div>
3. 变量与文本插入
- 变量传递:通过Express的
res.render(template, {变量键值对})传递。 - 变量插入:
=:插入变量并自动转义HTML(防XSS,推荐默认使用)。!=:插入变量但不转义HTML(有XSS风险,仅用于可信内容)。
- 文本块:使用
|表示纯文本行,或使用.开启多行文本块。
示例:变量与文本
//- 假设传递的变量:{ username: '<span>Alice</span>', age: 25 }
p 用户名:= username // 转义HTML
p 原始用户名:!= username // 不转义HTML
p 年龄:#{age} // 插值语法(等价于=)
//- 多行文本块
p.
这是第一行文本
这是第二行文本
会被编译为同一<p>标签内的内容
编译后:
<p>用户名:<span>Alice</span></p>
<p>原始用户名:<span>Alice</span></p>
<p>年龄:25</p>
<p>这是第一行文本 这是第二行文本 会被编译为同一<p>标签内的内容</p>
4. 条件判断
支持if-else if-else逻辑,根据变量值动态渲染内容:
//- 假设变量:{ score: 85 }
if score >= 90
p 成绩:优秀
else if score >= 60
p 成绩:及格
else
p 成绩:不及格
//- 简化写法(单行)
p= score >= 60 ? '通过' : '未通过'
编译后:
<p>成绩:及格</p>
<p>通过</p>
5. 循环迭代
通过each遍历数组或对象,支持索引和键名:
//- 假设变量:{ fruits: ['苹果', '香蕉', '橙子'] }
ul
each fruit, index in fruits
li= `第${index+1}个水果:${fruit}`
//- 遍历对象(假设变量:{ user: { name: 'Bob', age: 30 } })
dl
each value, key in user
dt= key
dd= value
编译后:
<ul>
<li>第1个水果:苹果</li>
<li>第2个水果:香蕉</li>
<li>第3个水果:橙子</li>
</ul>
<dl>
<dt>name</dt>
<dd>Bob</dd>
<dt>age</dt>
<dd>30</dd>
</dl>
6. 模板继承与块(Block)
通过extends和block实现模板复用,适合统一页面布局(如header、footer):
- 父模板(layout.pug):定义公共布局和可替换块
//- views/layout.pug
doctype html
html
head
meta(charset="UTF-8")
title #{pageTitle || '默认标题'}
block styles // 样式块(子模板可覆盖)
link(rel="stylesheet" href="/css/base.css")
body
header
h1 网站标题
main
block content // 内容块(子模板必须覆盖)
footer
p © 2023 网站版权
block scripts // 脚本块(子模板可添加)
- 子模板(home.pug):继承父模板并填充内容
//- views/home.pug
extends layout // 继承父模板
//- 覆盖页面标题变量
- var pageTitle = '首页'
//- 覆盖样式块(可选)
block styles
extends block styles // 保留父模板的样式
link(rel="stylesheet" href="/css/home.css")
//- 填充内容块(必须)
block content
h2 欢迎来到首页
p 这是首页的具体内容
//- 添加脚本块(可选)
block scripts
script(src="/js/home.js")
编译后:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
<link rel="stylesheet" href="/css/base.css">
<link rel="stylesheet" href="/css/home.css">
</head>
<body>
<header>
<h1>网站标题</h1>
</header>
<main>
<h2>欢迎来到首页</h2>
<p>这是首页的具体内容</p>
</main>
<footer>
<p>© 2023 网站版权</p>
</footer>
<script src="/js/home.js"></script>
</body>
</html>
7. 混入(Mixin):组件化复用
mixin用于定义可复用的代码片段(类似组件),支持参数传递:
//- 定义混入(可放在单独文件中,通过include引入)
mixin button(text, type='primary')
button(class=`btn btn-${type}`)= text
mixin card(title, content)
div.card
h3.card-title= title
p.card-content= content
//- 使用混入
+button('提交', 'success') // 传递参数
+button('取消') // 使用默认参数
+card('新闻标题', '这是新闻内容...')
编译后:
<button class="btn btn-success">提交</button>
<button class="btn btn-primary">取消</button>
<div class="card">
<h3 class="card-title">新闻标题</h3>
<p class="card-content">这是新闻内容...</p>
</div>
8. 引入其他文件(Include)
通过include引入外部Pug文件或静态资源(如HTML片段、Markdown):
//- 引入公共导航(views/partials/nav.pug)
include partials/nav
//- 引入Markdown文件(需配置过滤器,见下文)
include:markdown README.md
四、实战案例:构建动态博客页面
结合Express和Pug构建一个简单的博客列表页面,展示完整流程:
1. 项目结构
/blog-demo
/views
layout.pug # 父模板
index.pug # 首页(博客列表)
/partials
header.pug # 头部组件
footer.pug # 底部组件
/public
/css
style.css # 样式文件
app.js # 入口文件
package.json
2. 核心代码
(1)入口文件(app.js)
const express = require('express');
const app = express();
// 配置Pug
app.set('view engine', 'pug');
app.set('views', './views');
// 静态资源目录
app.use(express.static('public'));
// 模拟博客数据
const blogs = [
{ id: 1, title: 'Pug模板引擎入门', author: '张三', date: '2023-01-01' },
{ id: 2, title: 'Express与Pug结合实战', author: '李四', date: '2023-01-10' }
];
// 首页路由
app.get('/', (req, res) => {
res.render('index', {
pageTitle: '博客列表',
blogs: blogs
});
});
app.listen(3000, () => {
console.log('博客服务器运行在 http://localhost:3000');
});
(2)父模板(layout.pug)
doctype html
html
head
meta(charset="UTF-8")
title= pageTitle
link(rel="stylesheet" href="/css/style.css")
body
include partials/header
main.container
block content
include partials/footer
(3)头部组件(partials/header.pug)
header
nav
ul
li: a(href="/") 首页
li: a(href="/about") 关于
li: a(href="/contact") 联系
(4)首页模板(index.pug)
extends layout
block content
h2 最新博客
if blogs.length > 0
.blog-list
each blog in blogs
.blog-item
h3: a(href=`/blog/${blog.id}`)= blog.title
p 作者:#{blog.author} · 日期:#{blog.date}
else
p 暂无博客文章
(5)样式文件(public/css/style.css)
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.blog-list {
margin-top: 20px;
}
.blog-item {
border-bottom: 1px solid #eee;
padding: 15px 0;
}
.blog-item h3 {
margin: 0 0 10px 0;
}
3. 运行效果
启动服务后访问http://localhost:3000,将显示包含两篇博客的列表页面,布局统一且代码简洁。
五、最佳实践
1. 保持缩进一致性
Pug依赖缩进表示层级,建议:
- 使用2或4个空格(不要混合空格和制表符)。
- 编辑器开启“显示空白字符”功能,避免缩进错误。
- 大型项目可通过ESLint(配合
eslint-plugin-pug)检测缩进问题。
2. 合理组织模板结构
- 采用“父模板+子模板+组件”的分层结构:
layout.pug:全局布局(包含header、footer、公共脚本)。views/:按页面类型存放子模板(如home.pug、blog.pug)。views/partials/:存放可复用组件(如导航、卡片、按钮)。
- 通过
include引入组件,通过extends实现布局继承,减少代码重复。
3. 控制模板中的逻辑复杂度
- 模板应专注于“展示数据”,避免包含复杂业务逻辑(如数据过滤、计算)。
- 复杂逻辑应在Express控制器中处理,仅将处理后的结果传递给模板。
- 简单判断(如
if)和循环(each)是可接受的,但避免嵌套过深(建议不超过3层)。
4. 安全使用非转义插入(!=)
- 优先使用
=或#{}(自动转义HTML),防止XSS攻击。 - 仅在内容完全可信(如服务器生成的HTML)时使用
!=,例如://- 危险:用户输入的内容 p!= userInput // 若userInput包含<script>,会被执行 //- 安全:服务器生成的HTML p!= marked(article.content) // 假设article.content是可信的Markdown源
5. 利用过滤器扩展功能
Pug支持通过过滤器处理特定格式内容(如Markdown、CoffeeScript),需安装对应依赖:
npm install pug-cli marked --save-dev # marked用于Markdown转换
//- 定义过滤器(在模板中或单独配置文件)
filters:
markdown: require('marked').parse
//- 使用过滤器
:markdown
# Markdown标题
这是**Markdown**内容,会被转换为HTML
六、注意事项
1. 名称变更:Jade → Pug
- 2016年Jade因商标问题更名为Pug,旧版
jade包已停止维护,必须使用pug包。 - 迁移注意:大部分语法兼容,但部分旧特性(如
case语句)已调整,需参考官方迁移指南。
2. 缩进错误的排查
缩进错误是Pug最常见的问题,表现为编译失败(IndentationError),排查方法:
- 检查同一层级的缩进是否一致(如都是2个空格)。
- 确保嵌套元素的缩进比父元素多(如子元素比父元素多2个空格)。
- 避免混合使用空格和制表符(建议统一用空格)。
3. 与HTML的转换差异
- Pug不支持HTML的自闭合标签写法(如
<img/>),统一写为img。 - HTML中的
<!-- 注释 -->在Pug中写为//-(不会输出到HTML)或//(会输出为HTML注释)。 - 布尔属性(如
disabled、checked)在Pug中只需写属性名:input(type="checkbox" checked)。
4. 性能优化
- 开启模板缓存:在生产环境中,Express会自动缓存编译后的Pug模板(
app.enable('view cache'))。 - 减少模板嵌套深度:过深的嵌套会增加编译和渲染时间。
- 复用混入和组件:避免在多个模板中重复定义相同逻辑。
5. 调试技巧
- 开发环境中,设置
app.locals.pretty = true可让编译后的HTML格式化(便于调试):if (app.get('env') === 'development') { app.locals.pretty = true; } - 使用
console.log在模板中输出变量(需用-前缀表示代码行):- console.log('传递的变量:', user) // 输出到终端
七、总结
Jade(Pug)模板引擎以其“极简缩进语法”和“强大动态渲染能力”,为Node.js Web开发提供了高效的视图层解决方案。其核心优势在于:
- 简化代码:通过缩进替代HTML标签嵌套,减少冗余,提升开发效率。
- 组件化复用:通过模板继承、混入和引入机制,实现视图代码的模块化管理。
- 动态渲染:无缝集成变量、条件、循环等逻辑,将数据与视图自然结合。
掌握Pug的关键在于适应其缩进式语法,并通过合理的模板结构设计(如父模板+组件)提升代码可维护性。同时需注意名称变更(使用pug包)、缩进一致性和XSS安全风险,在实际项目中平衡简洁性与安全性。
对于需要快速构建动态网页的场景(如博客、CMS、后台管理系统),Pug是一个值得选择的工具——它不仅能减少代码量,更能让视图层逻辑清晰、易于扩展,是Node.js开发者构建优雅Web应用的得力助手。
1376

被折叠的 条评论
为什么被折叠?



