EJS模板 #

一、EJS简介 #

1.1 什么是EJS? #

EJS(Embedded JavaScript)是一种简单的模板语言,让你可以使用纯JavaScript语法生成HTML。

1.2 特点 #

  • 语法简单,类似HTML
  • 支持JavaScript所有语法
  • 支持模板包含和继承
  • 性能良好

1.3 安装 #

bash
npm install ejs

二、基本配置 #

2.1 设置视图引擎 #

javascript
const express = require('express');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');

2.2 自定义文件扩展名 #

javascript
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

三、EJS语法 #

3.1 输出标签 #

标签 说明 示例
<%= %> 输出(转义) <%= name %>
<%- %> 输出(不转义) <%- html %>
<% %> 执行代码 <% if (user) { %>
<%# %> 注释 <%# 注释内容 %>
-%> 去除换行 <% code -%>

3.2 输出变量 #

html
<p><%= name %></p>
<p><%= user.name %></p>
<p><%= users[0].name %></p>

3.3 条件语句 #

html
<% if (user) { %>
    <p>欢迎, <%= user.name %></p>
<% } else { %>
    <p>请登录</p>
<% } %>

3.4 循环语句 #

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

3.5 switch语句 #

html
<% switch (status) { %>
    <% case 'active': %>
        <span class="badge-success">激活</span>
        <% break %>
    <% case 'inactive': %>
        <span class="badge-warning">未激活</span>
        <% break %>
    <% default: %>
        <span class="badge-secondary">未知</span>
<% } %>

四、包含文件 #

4.1 include语法 #

html
<%- include('header') %>
<%- include('partials/nav', { user: user }) %>
<main>内容</main>
<%- include('footer') %>

4.2 文件结构 #

text
views/
├── partials/
│   ├── header.ejs
│   ├── nav.ejs
│   └── footer.ejs
├── index.ejs
└── about.ejs

4.3 header.ejs #

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title><%= title %></title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>

4.4 footer.ejs #

html
    <script src="/js/main.js"></script>
</body>
</html>

4.5 使用示例 #

html
<%- include('partials/header', { title: '首页' }) %>
<%- include('partials/nav') %>

<div class="container">
    <h1><%= message %></h1>
</div>

<%- include('partials/footer') %>

五、布局模板 #

5.1 安装express-ejs-layouts #

bash
npm install express-ejs-layouts

5.2 配置 #

javascript
const expressLayouts = require('express-ejs-layouts');

app.use(expressLayouts);
app.set('layout', 'layouts/main');

5.3 布局文件 #

views/layouts/main.ejs:

html
<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
    <%- include('partials/head') %>
</head>
<body>
    <%- include('partials/header') %>
    <main>
        <%- body %>
    </main>
    <%- include('partials/footer') %>
</body>
</html>

5.4 页面模板 #

views/index.ejs:

html
<h1>首页</h1>
<p>欢迎来到我的网站</p>

5.5 路由 #

javascript
app.get('/', (req, res) => {
    res.render('index', { title: '首页' });
});

六、过滤器 #

6.1 自定义过滤器 #

javascript
const ejs = require('ejs');

ejs.filters.trim = (str) => str.trim();
ejs.filters.upper = (str) => str.toUpperCase();
ejs.filters.lower = (str) => str.toLowerCase();
ejs.filters.date = (date) => new Date(date).toLocaleDateString();

6.2 使用过滤器 #

html
<p><%=: name | trim | upper %></p>
<p><%=: createdAt | date %></p>

七、辅助函数 #

7.1 注册辅助函数 #

javascript
app.locals.formatDate = (date) => {
    return new Date(date).toLocaleDateString('zh-CN');
};

app.locals.truncate = (str, length) => {
    if (str.length > length) {
        return str.substring(0, length) + '...';
    }
    return str;
};

app.locals.eq = (a, b) => a === b;

7.2 使用辅助函数 #

html
<p><%= formatDate(user.createdAt) %></p>
<p><%= truncate(post.content, 100) %></p>

<% if (eq(status, 'active')) { %>
    <span>激活</span>
<% } %>

八、完整示例 #

8.1 项目结构 #

text
views/
├── layouts/
│   └── main.ejs
├── partials/
│   ├── header.ejs
│   ├── footer.ejs
│   └── nav.ejs
├── index.ejs
├── users/
│   ├── index.ejs
│   └── show.ejs
└── posts/
    ├── index.ejs
    └── show.ejs

8.2 布局文件 #

views/layouts/main.ejs:

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= title %> - <%= siteName %></title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <%- include('../partials/header') %>
    
    <div class="container mt-4">
        <% if (messages.error) { %>
            <div class="alert alert-danger"><%= messages.error %></div>
        <% } %>
        <% if (messages.success) { %>
            <div class="alert alert-success"><%= messages.success %></div>
        <% } %>
        
        <%- body %>
    </div>
    
    <%- include('../partials/footer') %>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

8.3 用户列表页面 #

views/users/index.ejs:

html
<div class="d-flex justify-content-between align-items-center mb-4">
    <h2>用户列表</h2>
    <a href="/users/new" class="btn btn-primary">添加用户</a>
</div>

<table class="table table-striped">
    <thead>
        <tr>
            <th>ID</th>
            <th>用户名</th>
            <th>邮箱</th>
            <th>角色</th>
            <th>创建时间</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        <% users.forEach(user => { %>
            <tr>
                <td><%= user.id %></td>
                <td><%= user.name %></td>
                <td><%= user.email %></td>
                <td>
                    <span class="badge <%= user.role === 'admin' ? 'bg-danger' : 'bg-primary' %>">
                        <%= user.role %>
                    </span>
                </td>
                <td><%= formatDate(user.createdAt) %></td>
                <td>
                    <a href="/users/<%= user.id %>" class="btn btn-sm btn-info">查看</a>
                    <a href="/users/<%= user.id %>/edit" class="btn btn-sm btn-warning">编辑</a>
                    <form action="/users/<%= user.id %>?_method=DELETE" method="POST" style="display: inline;">
                        <button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('确定删除?')">删除</button>
                    </form>
                </td>
            </tr>
        <% }); %>
    </tbody>
</table>

<% if (totalPages > 1) { %>
    <nav>
        <ul class="pagination justify-content-center">
            <% if (currentPage > 1) { %>
                <li class="page-item">
                    <a class="page-link" href="/users?page=<%= currentPage - 1 %>">上一页</a>
                </li>
            <% } %>
            
            <% for (let i = 1; i <= totalPages; i++) { %>
                <li class="page-item <%= currentPage === i ? 'active' : '' %>">
                    <a class="page-link" href="/users?page=<%= i %>"><%= i %></a>
                </li>
            <% } %>
            
            <% if (currentPage < totalPages) { %>
                <li class="page-item">
                    <a class="page-link" href="/users?page=<%= currentPage + 1 %>">下一页</a>
                </li>
            <% } %>
        </ul>
    </nav>
<% } %>

8.4 路由 #

javascript
app.get('/users', async (req, res) => {
    const page = parseInt(req.query.page) || 1;
    const limit = 10;
    
    const users = await User.find()
        .skip((page - 1) * limit)
        .limit(limit);
    
    const total = await User.countDocuments();
    const totalPages = Math.ceil(total / limit);
    
    res.render('users/index', {
        title: '用户列表',
        users,
        currentPage: page,
        totalPages
    });
});

九、总结 #

EJS要点:

语法 说明
<%= %> 转义输出
<%- %> 不转义输出
<% %> 执行代码
include 包含文件
app.locals 全局变量

下一步,让我们学习Pug模板!

最后更新:2026-03-28