当前位置: 首页 > news >正文

1.17 模板引擎EJS

在 Node.js 中,模板引擎用于将动态数据与静态模板结合,生成最终的 HTML 页面。

一、核心概念

1. 模板引擎的作用
  • 将动态数据(如数据库查询结果)注入到静态模板中。
  • 分离视图逻辑与业务逻辑。
  • 提供安全的 HTML 转义,防止 XSS 攻击。
2. 基本工作流程
  1. 定义模板:创建包含占位符的 HTML 文件。
  2. 准备数据:从数据库或 API 获取动态数据。
  3. 渲染模板:将数据与模板结合,生成最终 HTML。
  4. 返回响应:将 HTML 发送给客户端。

二、主流模板引擎对比

特性EJSPug (Jade)HandlebarsNunjucks
语法风格HTML 内嵌标签缩进式语法双大括号语法类似 Jinja2
学习曲线低(HTML 友好)中等中等
性能中等
生态系统丰富丰富丰富中等

三、EJS(Embedded JavaScript)

1. 特点

  • 语法与 HTML 高度兼容。
  • 使用 <% %> 和 <%= %> 标签嵌入 JavaScript。
  • 支持条件语句、循环和自定义函数。

2. 安装与使用

npm install ejs
const express = require('express');
const app = express();// 设置 EJS 为视图引擎
app.set('view engine', 'ejs');// 渲染模板
app.get('/', (req, res) => {const data = {title: 'EJS 示例',users: ['Alice', 'Bob', 'Charlie']};res.render('index', data); // 渲染 views/index.ejs
});

3. 模板示例(index.ejs)

<!DOCTYPE html>
<html>
<head><title><%= title %></title>
</head>
<body><h1><%= title %></h1><ul><% users.forEach(user => { %><li><%= user %></li><% }); %></ul><% if (users.length > 2) { %><p>用户数量超过 2 人</p><% } %>
</body>
</html>

安全与性能考虑

1. HTML 转义
  • 自动转义:大多数模板引擎默认转义 HTML 特殊字符(如 < 转为 &lt;),防止 XSS 攻击。
    <!-- EJS 示例 -->
    <p><%= userInput %></p> <!-- 自动转义 -->
    <p><%- userInput %></p> <!-- 不转义(危险!) -->
    
2. 性能优化
  • 缓存编译:生产环境中启用模板缓存。

    // Express 配置缓存
    app.set('view cache', process.env.NODE_ENV === 'production');
    
  • 避免复杂逻辑:将业务逻辑放在控制器中,保持模板简单。

四、EJS详细使用介绍

一、核心概念

1. 模板引擎的作用
  • 将动态数据(如数据库查询结果)与静态 HTML 模板结合。
  • 分离视图逻辑与业务逻辑,提高代码可维护性。
2. EJS 的特点
  • 语法简单:直接在 HTML 中嵌入 JavaScript,无需学习新语法。
  • 灵活性高:支持所有 JavaScript 表达式和逻辑。
  • 性能优异:编译后的模板执行速度快。
  • 与 Express 集成良好:是 Express 官方推荐的模板引擎之一。

二、基础语法

1. 输出变量:<%= ... %>
<!-- 渲染变量 -->
<h1>Hello, <%= username %>!</h1> <!-- 输出变量值,自动转义 HTML --><!-- 执行表达式 -->
<p>2 + 2 = <%= 2 + 2 %></p>
2. 执行 JavaScript 代码:<% ... %>
<!-- 条件判断 -->
<% if (user.isAdmin) { %><p>管理员视图</p>
<% } else { %><p>普通用户视图</p>
<% } %><!-- 循环 -->
<ul><% users.forEach(user => { %><li><%= user.name %></li><% }); %>
</ul>
3. 不转义输出:<%- ... %>
<!-- 输出 HTML 代码(危险!需确保内容安全) -->
<div><%- htmlContent %></div>

三、与 Express 集成

1. 安装与配置
npm install ejs
const express = require('express');
const app = express();// 设置 EJS 为视图引擎
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views'); // 视图文件目录// 渲染模板
app.get('/', (req, res) => {res.render('index', {title: 'EJS 示例',users: [{ id: 1, name: 'Alice' },{ id: 2, name: 'Bob' },{ id: 3, name: 'Charlie' }]});
});
2. 目录结构
project/
├── app.js
├── package.json
└── views/├── index.ejs└── partials/└── header.ejs

四、高级用法

1. 包含子模板:<%- include() %>
<!-- views/index.ejs -->
<!DOCTYPE html>
<html>
<head><title><%= title %></title><%- include('partials/header') %> <!-- 包含 header.ejs -->
</head>
<body><h1><%= title %></h1><%- include('partials/users', { users }) %> <!-- 传递数据到子模板 -->
</body>
</html>
2. 自定义过滤器
// app.js 中注册过滤器
app.locals.formatDate = (date) => {return new Date(date).toLocaleDateString();
};
<!-- 在模板中使用 -->
<p>创建时间:<%= formatDate(post.createdAt) %></p>
3. 布局模板(通过自定义实现)
// app.js 中添加布局功能
app.use((req, res, next) => {res.renderWithLayout = (view, data) => {data = data || {};data.content = () => include(view);res.render('layouts/main', data);};next();
});
<!-- layouts/main.ejs -->
<!DOCTYPE html>
<html>
<head><title><%= title %></title>
</head>
<body><header>导航栏</header><main><%- content() %></main> <!-- 插入子视图 --><footer>页脚</footer>
</body>
</html>

五、安全考虑

1. HTML 转义
  • 默认转义:使用 <%= ... %> 自动转义 HTML 特殊字符(如 < 转为 &lt;),防止 XSS 攻击。
  • 不转义:仅在信任内容时使用 <%- ... %>,如:
    <%- trustedHtml %> <!-- 仅用于安全的 HTML 内容 -->
    
2. 数据验证
  • 在渲染前验证和清理数据,避免恶意内容:
    app.get('/profile', (req, res) => {const username = req.query.username.replace(/[^a-zA-Z0-9]/g, ''); // 清理输入res.render('profile', { username });
    });
    

六、性能优化

1. 模板缓存
  • 在生产环境中启用模板缓存:
    app.set('view cache', process.env.NODE_ENV === 'production');
    
2. 预编译模板
  • 使用 ejs.compile() 预编译模板,减少运行时开销:
    const template = ejs.compile(fs.readFileSync('template.ejs', 'utf8'));
    const html = template({ data });
    

七、常见问题与解决方案

1. 变量未定义错误
  • 确保传递所有必要变量到模板:
    res.render('profile', { user: req.user }); // 确保 user 存在
    
2. 长逻辑处理
  • 将复杂逻辑移至控制器或辅助函数,保持模板简洁:
    // app.js
    app.locals.getTotal = (items) => {return items.reduce((sum, item) => sum + item.price, 0);
    };
    
3. 异步数据加载
  • 在渲染前确保所有数据已加载:
    app.get('/posts', async (req, res) => {const posts = await Post.find();res.render('posts', { posts });
    });
    

八、完整示例

1. 模板文件(views/index.ejs)
<!DOCTYPE html>
<html>
<head><title><%= title %></title>
</head>
<body><h1><%= title %></h1><%- include('partials/message', { message }) %><ul><% users.forEach(user => { %><li><strong><%= user.name %></strong><p><%= user.email %></p><% if (user.isAdmin) { %><span class="badge">管理员</span><% } %></li><% }); %></ul>
</body>
</html>
2. 控制器(app.js)
app.get('/', (req, res) => {const users = [{ name: 'Alice', email: 'alice@example.com', isAdmin: true },{ name: 'Bob', email: 'bob@example.com', isAdmin: false }];res.render('index', {title: '用户列表',users,message: '欢迎查看用户列表'});
});

总结

EJS 的核心优势在于 简单直接 和 与 JavaScript 的无缝集成,适合需要快速开发、灵活控制渲染逻辑的项目。通过合理使用模板包含、过滤器和布局,可以构建结构清晰、可维护的视图层。

推荐场景

  • 前后端不分离的 Web 应用。
  • 对性能有要求的项目(编译后的模板执行效率高)。
  • 团队熟悉 JavaScript/HTML,希望降低学习成本。

相关文章:

  • 如何可视化机器学习模型:从线性回归到神经网络
  • 学习日记-day30-6.15
  • 山东大学软件学院创新项目实训开发日志——第十七周(二)
  • 手写muduo网络库(九):TcpConnection
  • 如何使用configure脚本安装PBS
  • 图形编辑器基于Paper.js教程29:基于图层的所有矢量图元的填充规则实现
  • 组策略关闭 Windows 防火墙指南(企业版/专业版)
  • SpringMVC系列(一)(介绍,简单应用以及路径位置通配符)
  • 机器学习实验报告5-K-means 算法
  • Linux--存储系统探秘:从块设备到inode
  • 影视剧学经典系列-梁祝-陶渊明《感士不遇赋并序》
  • Appium+python自动化(二十三)- Monkeyrunner与Monkey
  • React forwardRef 与 useImperativeHandle 深度解析
  • selenium点击元素出现的obscure问题
  • 设计模式精讲 Day 2:工厂方法模式(Factory Method Pattern)
  • 什么是敏捷中的迭代(Iteration)和 Sprint?
  • 计算机硬件——主板
  • 【旧题新解】第 9 集 带余除法
  • Java 常用类 Arrays:从零到实战的数组操作指南
  • ArkUI-X框架LogInterface使用指南
  • 公路建设新闻网站/引流推广效果好的app
  • 中国空间站真实图片/搜索引擎优化时营销关键词
  • 我不想找之前做网站的续费/关键词排名怎么做上去
  • 日本网站制作/关键词优化排名第一
  • 网站备案是给什么进行备案/免费网站流量统计工具
  • 做行程的网站 哪个最好/seo快速排名