EJS适合快速开发且接近HTML,Pug语法简洁但缩进敏感,Handlebars逻辑分离严格需自定义helper;三者选型取决于项目需求与团队习惯。

模板引擎选哪个:EJS、Pug 和 Handlebars 的实际取舍
没有“最好”的模板引擎,只有更匹配你当前项目的那个。EJS 最接近原生 HTML,写起来快,适合快速原型或已有 HTML 基础的团队;Pug(原 Jade)缩进驱动、语法精简,但新人容易因空格报错;Handlebars 语法严格、逻辑剥离干净,适合前后端分离后由前端主导渲染的场景。
常见错误现象:ReferenceError: template is not defined 往往是没正确 require 模板模块,或路径拼错导致 fs.readFileSync 返回 undefined。
- EJS 默认不转义 HTML,用
输出原始内容,才转义——这点常被忽略,导致 XSS 风险 - Pug 中
each循环必须缩进对齐,多一个空格或少一个都会抛unexpected token "indent" - Handlebars 不支持在模板里写 JS 表达式,
{{if user.admin}}是非法的,得靠注册自定义 helper:Handlebars.registerHelper('ifAdmin', ...)
Node.js 中用 EJS 渲染带数据的 HTML 页面
Express 默认不内置模板引擎,需手动配置。关键不是“怎么装”,而是“怎么让 Express 正确识别并传入数据”。
实操要点:
- 安装:
npm install ejs,然后在 Express 初始化时设置:app.set('view engine', 'ejs')和app.set('views', path.join(__dirname, 'views')) - 路由中用
res.render('index', { title: '首页', items: ['a', 'b'] }),注意第二个参数必须是对象,不能是数组或字符串 -
index.ejs文件必须放在views/目录下,否则报Failed to lookup view - 如果想用子目录如
views/admin/index.ejs,调用时写res.render('admin/index', {...}),路径不用加扩展名
静态资源路径错乱:public 目录和模板中 href/src 怎么配
模板渲染出的 HTML 里, 能加载,不代表你本地 public/css/app.css 就自动生效——Express 必须显式声明静态服务中间件。
传媒企业网站系统使用热腾CMS(RTCMS),根据网站板块定制的栏目,如果修改栏目,需要修改模板相应的标签。站点内容均可在后台网站基本设置中添加。全站可生成HTML,安装默认动态浏览。并可以独立设置SEO标题、关键字、描述信息。源码包中带有少量测试数据,安装时可选择演示安装或全新安装。如果全新安装,后台内容充实后,首页才能完全显示出来。(全新安装后可以删除演示数据用到的图片,目录在https://
典型遗漏点:
- 忘了加
app.use(express.static('public')),结果所有/css/xxx请求返回 404 - 模板中写了
,但实际文件在
public/images/logo.png,应写成
- 使用相对路径如
./css/app.css在模板里会随路由变化失效,一律用以/开头的绝对路径 - EJS 中动态拼接路径别直接插变量:
要确保theme是可信值,否则可能引入恶意路径
模板里调用函数:什么时候该用 filter,什么时候该预处理数据
模板不是 JavaScript 执行环境,它的职责是展示,不是计算。把格式化逻辑塞进模板,会让调试变难、复用变差、测试变空。
推荐做法:
- 日期格式化、金额千分位等通用操作,提前在路由或 service 层处理好,传入已加工的字段,比如
{ priceFormatted: '$1,299.00' } - 确实需要模板内调用(如 i18n 翻译),用 EJS 的
定义局部函数,或在app.locals上挂全局工具函数 - Handlebars 中必须用 helper,且 helper 函数不能访问
this外的数据,参数全靠模板传入,{{formatDate date format='YYYY-MM-DD'}}是安全边界 - 避免在模板里做 API 请求、数据库查询、复杂循环嵌套——这些行为既慢又不可测
最常被绕开的问题是:模板编译缓存。开发时改了 .ejs 文件却看不到效果?检查是否启用了 app.set('view cache', true)(生产默认开启,开发建议关掉)。还有,res.render() 报错堆栈里如果没显示具体哪行模板出问题,多半是 EJS 编译阶段失败,得看 console.error 输出的第一行错误位置。









