Skip to content

EJS 模板引擎

后端模板 EJS Express.js

一、什么是 EJS

1.1 定义

EJS = Embedded JavaScript — 在 HTML 中嵌入 JavaScript 代码的模板语言。

1.2 为什么需要模板

问题res.send 中写大量 HTML 混乱不堪 → 违反关注点分离。

javascript
// ❌ 结构与逻辑混在一起
res.send(`
  <html><head><link rel="stylesheet" href="..."></head>
  <body><h1>Hello ${name}</h1><p>Welcome...</p></body></html>
`);

// ✅ 用 EJS 分离:服务器传数据,模板管展示
res.render("index.ejs", { name: "Hugo" });

1.3 工作流程

1. 用户提交表单 → POST 请求到服务器
2. 服务器处理数据(req.body)
3. 服务器 res.render("template.ejs", { data })
4. EJS 将 data 注入模板 → 生成 HTML
5. 浏览器渲染最终 HTML

二、安装与配置

bash
npm i ejs

EJS 文件必须放在 views/ 目录下:

project/
├── index.js
├── package.json
├── public/          ← 静态文件
│   ├── styles/
│   └── images/
└── views/           ← EJS 模板(必须叫 views)
    ├── index.ejs
    ├── about.ejs
    └── partials/
        ├── header.ejs
        └── footer.ejs

VS Code 插件:安装 "EJS Language Support"(Digital Brain Stem)以获得语法高亮。


三、EJS 标签语法

3.1 六种标签

标签名称功能示例
<%= %>输出标签执行 JS 并 输出结果<%= name %>Angela
<% %>代码标签执行 JS 不输出<% if (true) { %>
<%- %>HTML 标签输出 不转义的 HTML<%- "<h1>Hi</h1>" %> → 渲染 H1
<%% %>转义标签显示 EJS 标签本身<%%= %> → 显示 <%= %>
<%# %>注释标签EJS 注释(不渲染)<%# 这是注释 %>
<%- include() %>包含标签引入其他 EJS 文件<%- include("partials/header.ejs") %>

3.2 输出标签 <%= %>

html
<!-- 变量输出 -->
<h1>Hello, <%= name %>!</h1>

<!-- 表达式输出 -->
<p>You have <%= items.length %> items.</p>

3.3 代码标签 <% %>

每行 JS 代码 都需要独立的 EJS 标签:

html
<ul>
  <% for (let i = 0; i < items.length; i++) { %>
    <li><%= items[i] %></li>
  <% } %>
</ul>

forEach 写法:

html
<ul>
  <% items.forEach(function(fruit) { %>
    <li><%= fruit %></li>
  <% }); %>
</ul>

if-else:

html
<% if (seconds % 2 === 0) { %>
  <ul>
    <% items.forEach(function(item) { %>
      <li><%= item %></li>
    <% }); %>
  </ul>
<% } else { %>
  <p>No items to display.</p>
<% } %>

常见错误

html
<!-- ❌ 不能用一个标签包裹所有 JS -->
<% if (condition) {
  // HTML goes here
} %>

<!-- ✅ 每部分 JS 都需要独立标签 -->
<% if (condition) { %>
  <p>HTML content</p>
<% } %>

3.4 HTML 标签 <%- %>

html
<!-- 变量含 HTML 标记,会被渲染为实际 HTML -->
<%- "<strong>Bold Text</strong>" %>
<!-- 输出: Bold Text(加粗显示) -->

<!-- 对比:输出标签会转义 HTML -->
<%= "<strong>Bold Text</strong>" %>
<!-- 输出: <strong>Bold Text</strong>(原样显示) -->

四、数据传递

4.1 服务器 → 模板(res.render)

javascript
app.get("/", (req, res) => {
  res.render("index.ejs", {
    title: "My Page",           // 字符串
    dayType: "weekday",         // 字符串
    items: ["Apple", "Banana"], // 数组
    htmlContent: "<em>Hello</em>" // HTML
  });
});

在 EJS 中使用:

html
<h1><%= title %></h1>
<p>Today is a <%= dayType %></p>
<ul>
  <% items.forEach(function(item) { %>
    <li><%= item %></li>
  <% }); %>
</ul>
<%- htmlContent %>

4.2 客户端 → 服务器(表单 POST)

HTML 表单:

html
<form action="/submit" method="POST">
  <label for="fName">First Name:</label>
  <input type="text" name="fName" required>

  <label for="lName">Last Name:</label>
  <input type="text" name="lName" required>

  <input type="submit" value="OK">
</form>

服务器接收:

javascript
app.use(bodyParser.urlencoded({ extended: true }));

app.post("/submit", (req, res) => {
  const firstName = req.body.fName;  // name 属性对应
  const lastName = req.body.lName;
  const numberOfLetters = firstName.length + lastName.length;

  res.render("index.ejs", { numberOfLetters: numberOfLetters });
});

4.3 处理未定义变量 — locals

html
<!-- ❌ 如果变量不存在会崩溃 -->
<% if (numberOfLetters) { %>

<!-- ✅ 用 locals 安全检查 -->
<% if (locals.numberOfLetters) { %>
  <h1>There are <%= numberOfLetters %> letters in your name.</h1>
<% } else { %>
  <h1>Enter your name below 👇</h1>
<% } %>

locals 的作用

locals 对象 始终存在,包含所有通过 res.render 传递的变量。

直接用变量名检查 if (numberOfLetters) 在 EJS 中会抛出 ReferenceError,但 locals.numberOfLetters 会安全返回 undefined


五、静态文件

5.1 配置 public 目录

javascript
// 告诉 Express 静态文件在 public/ 目录下
app.use(express.static("public"));

5.2 在 EJS 中引用

html
<!-- CSS:路径相对于 public/ -->
<link rel="stylesheet" href="/styles/layout.css">

<!-- 图片 -->
<img src="/images/logo.png">

<!-- JS -->
<script src="/scripts/main.js"></script>

5.3 动态文件 vs 静态文件

类型位置特点示例
动态views/由服务器动态生成.ejs 模板
静态public/不变,直接提供CSS、图片、前端 JS

六、Partials(局部模板)

6.1 概念

Partials = 可复用的 EJS 代码片段(如 header、footer、navbar)。

目的:避免在每个页面重复相同的代码。

6.2 include 语法

html
<!-- 文件路径相对于 views/ 目录 -->
<%- include("partials/header.ejs") %>

<h1>Page Content</h1>
<p>This is the unique part of each page.</p>

<%- include("partials/footer.ejs") %>

6.3 项目结构

views/
├── index.ejs       ← include header + footer
├── about.ejs       ← include header + footer
├── contact.ejs     ← include header + footer
└── partials/
    ├── header.ejs  ← <html><head>... + nav bar
    └── footer.ejs  ← footer + </body></html>

header.ejs 示例:

html
<!DOCTYPE html>
<html>
<head>
  <title>My Website</title>
  <link rel="stylesheet" href="/styles/layout.css">
</head>
<body>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
  </nav>

footer.ejs 示例:

html
  <footer>
    <p>Copyright © 2026</p>
  </footer>
</body>
</html>

6.4 多页面路由

javascript
app.get("/", (req, res) => {
  res.render("index.ejs");
});

app.get("/about", (req, res) => {
  res.render("about.ejs");
});

app.get("/contact", (req, res) => {
  res.render("contact.ejs");
});

💡 HTML 中的 <a href="/about"> 等链接就是在发送 GET 请求 到对应路由。


七、完整项目:日期建议网站

7.1 服务器端(index.js)

javascript
import express from "express";

const app = express();
const port = 3000;

app.get("/", (req, res) => {
  const today = new Date();
  const day = today.getDay();  // 0=Sun, 1=Mon, ..., 6=Sat

  let type = "a weekday";
  let adv = "it's time to work hard";

  if (day === 0 || day === 6) {
    type = "the weekend";
    adv = "it's time to have some fun";
  }

  res.render("index.ejs", { dayType: type, advice: adv });
});

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

7.2 模板端(views/index.ejs)

html
<!DOCTYPE html>
<html>
<head><title>Weekday Warrior</title></head>
<body>
  <h1>
    Hey, it's <%= dayType %>,
    <%= advice %>!
  </h1>
</body>
</html>

八、EJS 速查表

操作代码
输出变量<%= variable %>
执行代码(无输出)<% code %>
输出 HTML(不转义)<%- htmlVariable %>
注释<%# comment %>
引入 partial<%- include("path/file.ejs") %>
安全检查变量<% if (locals.varName) { %>
渲染模板res.render("file.ejs", { key: value })
静态文件app.use(express.static("public"))

← 返回 Web 开发研究