首页
/ freeCodeCamp 高级 Node 与 Express 实战:用模板引擎(Pug)把服务端变量渲染到页面

freeCodeCamp 高级 Node 与 Express 实战:用模板引擎(Pug)把服务端变量渲染到页面

2026-09-04 09:34:09作者:齐冠琰

本文以 freeCodeCamp 课程「Advanced Node and Express(高级 Node 与 Express)」模块中的项目型挑战 Use a Template Engine's Powers 为主体,完整讲解如何在 Express 中通过 res.render 将服务端变量(titlemessage)传递给 Pug 模板并完成渲染。读完后,你将掌握 Pug 中变量插值的两种写法、Pug 的缩进语法与条件分支,以及该项目挑战的自动测试是如何校验你的页面输出,从而独立完成这一经典的教学项目。

挑战在 freeCodeCamp 课程体系中的位置

该挑战定义于 5895f70bf9fc0f352b528e64.md,其 frontmatter 声明了 challengeType: 2,即项目型(Project)挑战——你需要在一个真实可运行的项目代码库中动手完成功能,而不是只回答问答。项目型挑战的类型枚举定义见 challenge-types.ts

从课程结构文件 advanced-node-and-express.json 可以看到,该挑战是 advanced-node-and-express 块中的第二道题challengeOrder 数组第二项),紧跟在 Set up a Template Engine 之后;而该块整体隶属于 quality-assurance 超级块(见 quality-assurance.json),与 Quality Assurance and Testing with ChaiQuality Assurance Projects 并列。这一安排说明:模板引擎是本块的第一块基石,后续所有关于 Passport 认证、用户序列化、社交认证等内容都建立在此之上。

挑战文档说明,你需要通过以下方式之一完成代码:

  • 克隆配套的项目模板仓库 boilerplate-advancednode(freeCodeCamp 提供的 GitHub 仓库),在本地完成这些挑战;
  • 或使用你偏好的在线站点构建器,但务必纳入模板仓库中的全部文件。

回顾前置知识:模板引擎的作用与配置

在讲“传变量”之前,需要回顾上一题 Set up a Template Engine 中已经完成的三件事,它们是本题渲染生效的前提:

  1. pug@~3.0.0 已安装并写入项目的 package.json 依赖;
  2. 通过 Express 的 set 方法告知应用使用的视图引擎:
app.set('view engine', 'pug');
  1. views 指向 Pug 模板目录:
app.set('views', './views/pug');

模板引擎允许你在应用中直接使用静态模板文件(例如 Pug 编写)。运行时,模板引擎会把模板文件中的变量替换为服务端提供的真实值,再把模板转换成静态 HTML 发送给客户端。 这样做的好处是:HTML 页面的设计更简单,且无需客户端再发起 API 调用就能展示变量数据。

上一题的测试用例会通过 /_api/app 接口断言 app.settings['view engine']"pug"app.settings.views"./views/pug",并请求首页断言渲染出的 HTML 包含 FCC Advanced Node and Expresspug-success-message——这些配置若未正确完成,本题的变量渲染也就无从谈起。

Pug 中引用变量的两种写法

挑战文档的核心知识点是:模板引擎最强大的特性之一,就是允许在把模板渲染成 HTML 之前,把变量从服务端传入模板文件。 在 Pug 文件中有两种等价的引用方式:

方式一:内联插值 #{variable_name}

在元素内的文本中,用 #{variable_name} 的形式内联引用变量,可以与其他文本混排:

p Hello, #{name}!

方式二:元素级赋值 p=variable_name

在元素名后紧跟不带空格的等号,把变量的值直接赋给该元素的文本内容:

p=variable_name

注意这里的等号与元素名之间不能有空格,这是 Pug 语法的硬性要求:p = variable 会被解析为带 = 属性的元素,而 p=variable 才是内容赋值。

文档同时强调了 Pug 的哲学:用空白和制表符(缩进)来表达嵌套关系,从而大幅减少写出美观站点所需的代码量。 这正是 Pug 区别于传统 HTML 的关键——没有闭合标签,层级完全由缩进决定。

官方示例逐行解析:脚本内联与 if/else 分支

挑战文档给出了一段完整的 Pug 示例,它同时演示了两个高频场景:script 标签后跟 . 的内联文本块,以及基于服务端变量的 if/else 条件渲染。

head
  script(type='text/javascript').
    if (foo) bar(1 + 5);
body
  if youAreUsingPug
      p You are amazing
    else
      p Get on it!

对应的渲染结果为:

<head>
  <script type="text/javascript">
    if (foo) bar(1 + 5);
  </script>
</head>
<body>
  <p>You are amazing</p>
</body>

从源码结构看这段示例的几个要点:

  • script(type='text/javascript'). 末尾的点号表示其后缩进的内容是原样保留的文本块(literal block)。这样 if (foo) bar(1 + 5); 不会被当成 Pug 的控制流,而是作为 JavaScript 源码原封不动地输出到 <script> 中;
  • if youAreUsingPug 之后的 p You are amazing / else 分支是 Pug 模板自身的条件逻辑,它读取的是服务端传入的变量 youAreUsingPug,渲染时只有真值分支会被输出;
  • 示例 HTML 只出现 <p>You are amazing</p>,说明 youAreUsingPug 为真。这恰好印证了主题:分支走向由服务端注入的变量决定,模板本身不包含任何客户端请求逻辑。

实战步骤:把 title 和 message 传入 index.pug

你的项目模板中已经包含 index.pug 文件,其中使用到了两个变量:titlemessage。要让它们被渲染,需要在 Express 首页路由中调用 res.render,并把一个包含变量键值对的对象作为第二个参数传入。具体要求:

  • title 赋值为 Hello
  • message 赋值为 Please log in

即:

res.render('index', { title: 'Hello', message: 'Please log in' });

参数说明:

参数 含义 本例取值
第一个参数 模板名(不含扩展名),相对于 views 目录(./views/pug 'index' → 渲染 views/pug/index.pug
第二个参数 传入模板的变量对象,键名即模板中可引用的变量名 { title: 'Hello', message: 'Please log in' }

刷新页面后,你就应看到这些值按 index.pug 中的布局渲染到对应位置。挑战文档还提示:若你在本地遇到报错,可对照论坛中“已完成到这一步的项目”自查(原文档提供了论坛链接,本文按仓库引用规范不再列出)。

自动测试如何验证你的实现

挑战文档的 --hints-- 部分给出了自动测试的核心断言,它能帮我们精确理解“做对了”的判据:

const url = new URL("/", code);
const res = await fetch(url);
const data = await res.text();
assert.match(
  data,
  /pug-variable("|')>Please log in/gi,
  'Your projects home page should now be rendered by pug with the projects .pug file unaltered'
);

解读这段测试:

  1. 测试直接向运行中应用的根路径 / 发起 fetch 请求,取得返回的 HTML 文本——这验证的是服务端渲染的最终产物,而不是你的模板文件内容;
  2. 正则 /pug-variable("|')>Please log in/gi 匹配 pug-variable="Please log inpug-variable='Please log in 这类写法,即断言 message 的值 Please log in 出现在一个名为 pug-variable 的元素属性中,且引号形式不限、大小写不敏感(gi 标志);
  3. 断言失败信息中的“the projects .pug file unaltered”表明:你不能靠修改 index.pug 里写死文本来作弊,模板文件应保持原样,值必须来自 res.render 传入的第二个参数对象。

配合上一题的测试(断言首页包含 FCC Advanced Node and Expresspug-success-message,以及通过 /_api/app 校验 view engineviews 设置),整个模板引擎相关链路——依赖声明、应用设置、渲染调用、变量注入——都被逐层覆盖。

小结

围绕 Use a Template Engine's Powers 挑战文档 的核心脉络,本篇完整覆盖了:

  • 模板引擎的价值:运行时用服务端真实值替换模板变量,输出静态 HTML,免去客户端 API 调用;
  • Pug 的两种变量引用写法:内联 #{variable_name} 与元素赋值 p=variable_name(等号前不留空格);
  • 用缩进表达嵌套、用 . 保留脚本原文、用 if/else 做服务端条件渲染的完整示例及其生成的 HTML;
  • 动手操作:res.render('index', { title: 'Hello', message: 'Please log in' })
  • 测试判据:请求首页后,Please log in 必须出现在 pug-variable 属性中,且模板文件不得被篡改。

完成本题后,你可以在 advanced-node-and-express 挑战列表 中继续下一题 Set up Passport,开始用户认证的构建。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384