EJS不适合现代前端工程化项目,因其本质是Node.js服务端模板引擎,依赖res.render()在服务端执行输出HTML,无法参与Webpack/Vite构建流程,浏览器无法解析<% %>语法,导致白屏或ReferenceError。

为什么 EJS 不适合现代前端工程化项目
EJS 是服务端模板引擎,本质是 Node.js 运行时的 require('ejs') 模块,它依赖 res.render() 或 ejs.render() 在服务端执行并输出 HTML 字符串。这意味着它无法直接参与 Webpack/Vite 的构建流程,也不能被浏览器解析——打包后若保留 语法,会直接暴露在 HTML 中导致白屏或报错。
常见错误现象:ReferenceError: ejs is not defined(试图在浏览器里 require ejs)、Uncaught SyntaxError: Unexpected token '(服务端未渲染就发给前端的原始 .ejs 文件)。
使用场景仅限于 Express/Koa 等 Node.js 服务端渲染场景,比如 CMS 后台、内部管理页、邮件模板生成。若你正在用 Vue/React + Vite,强行引入 EJS 只会让构建变慢、热更新失效、SSR 配置混乱。
Express 中正确配置 EJS 的最小必要步骤
必须显式设置 view engine 和 views 目录,且路径需为绝对路径;否则 res.render() 找不到模板会抛出 Error: Failed to lookup view。
立即学习“前端免费学习笔记(深入)”;
- 在入口文件(如
app.js)中调用app.set('views', path.join(__dirname, 'views')) - 执行
app.set('view engine', 'ejs'),不要写成'ejs.html'或'.ejs' - 必须注册
app.engine('ejs', ejs.renderFile)(虽然 Express 默认支持,但显式声明可避免某些版本兼容问题) - 静态资源目录需单独设置:
app.use(express.static('public')),EJS 不处理 CSS/JS 加载逻辑
示例片段:
const express = require('express');
const ejs = require('ejs');
const app = express();
<p>app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.engine('ejs', ejs.renderFile); // 显式注册更稳妥
app.use(express.static('public'));</p><p>app.get('/', (req, res) => {
res.render('index', { title: 'Home' }); // 渲染 views/index.ejs
});EJS 模板里引用静态资源的坑
EJS 本身不解析 src 或 href 中的相对路径,它只是字符串替换。如果你写 <script src="./js/app.js"></script>,而服务器没配好静态路由,就会 404。
关键点:路径必须匹配 express.static() 的挂载位置。比如 app.use('/assets', express.static('public')),那模板里就得写 <link href="/assets/css/main.css">,而不是 ./css/main.css。
- 避免在 EJS 中用
__dirname或process.cwd()拼接路径——它们是服务端路径,和 URL 路径无关 - 不要依赖 EJS 的
<% include() %>加载 JS/CSS——它只做文本拼接,不会触发 HTTP 请求 - 如果用了 Webpack 构建前端资源,确保构建产物(如
dist/js/bundle.js)被express.static()正确托管,再在 EJS 中写对应 URL
如何让 EJS 支持局部变量作用域与防 XSS
EJS 默认不转义输出,<%= user.name %> 会直接插入 HTML,若 user.name 包含 <script> 就有风险。而 <%== user.name %> 是显式不转义,<%= user.name %> 实际上等价于 <%- user.name %>(即转义),但很多人误以为它不转义。
- 始终用
<%= value %>处理用户输入内容(自动 HTML 转义) - 仅当明确需要渲染 HTML 片段时,才用
<%== value %>,且必须配合服务端白名单过滤(例如用sanitize-html库预处理) - EJS 无原生作用域隔离,
<% const x = 1 %>声明的变量在同级模板内全局可见,嵌套 include 时容易覆盖变量——建议用<% const data = locals.data || {} %>显式限定作用域
真正复杂的页面逻辑,应该抽离到路由 handler 中处理,而不是塞进 EJS 的 <% %> 块里——那里只适合简单条件、循环和变量插值。



















