纯前端静态环境别硬上模板引擎,优先用 + js 克隆;需传参/条件渲染/循环时再选 mustache 或 handlebars;项目已用 webpack/vite 则配合 html-webpack-plugin 或 vite-plugin-html-inject 使用 ejs/pug。

直接说结论:纯前端静态环境别硬上模板引擎,优先用 <template></template> + JS 克隆;需要传参/条件渲染/循环,再考虑轻量级如 Mustache 或 Handlebars;项目已用构建工具(Webpack/Vite),就用 html-webpack-plugin 或 vite-plugin-html-inject 配合 EJS/Pug。
为什么 <!--#include--> 在本地双击打开时完全不生效
这不是浏览器问题,是服务器行为。Chrome 打开 file:// 协议下的 HTML 文件时,<!--#include file="header.html"--> 会被当作文本原样显示,甚至破坏 DOM 结构。
- Apache 需启用
Options +Includes并设置AddType text/html .html - Nginx 必须配置
ssi on;且响应头含Content-Type: text/html - Vercel、Netlify、GitHub Pages 等托管平台默认禁用 SSI,无法启用
- 真正生效的路径是服务端解析后返回的 HTML,不是你本地看到的源码
<template></template> 标签复用必须调用 content.cloneNode(true)
很多人写完 <template id="modal"><div>...</div></template>,直接 document.body.appendChild(template),结果第二次调用就报错或空白——因为 DOM 节点被移走了。
-
template.content是 DocumentFragment,appendChild()会移动节点而非复制 - 正确写法:
document.body.appendChild(template.content.cloneNode(true)) - 如果模板里含
<script></script>,cloneNode(true)不执行脚本,需手动创建并 append:const script = document.createElement('script'); script.textContent = template.querySelector('script').textContent; document.body.appendChild(script); - 不支持 {{if}}/{{each}},填数据得靠
el.querySelector('.title').textContent = data.title这类手动赋值
Handlebars compile() 渲染失败却没报错,通常卡在这几个地方
Handlebars.compile() 返回的是函数,执行它才真正出错或返回空字符串。静默失效比报错更难排查。
-
document.getElementById('tpl')拿到的是null—— 检查 DOM 是否加载完成(脚本放里记得加defer) - 模板字符串里有未闭合的
{{或多出的},compile()会返回一个执行即 throw 的函数,但错误堆栈指向调用处而非模板行 - 字段名大小写不一致:
{{user_name}}对应 JS 数据是{ userName: 'Alice' }→ 渲染为空,无提示 -
{{#if user}}中传入{ user: '' }或{ user: 0 },都被判为 falsy,块内容不渲染
EJS 和 Pug 在构建流程中才能真正“活起来”
把 index.ejs 直接扔进浏览器双击打开,只会看到原始语法文本(比如 ),因为浏览器根本不认识这种语法。
- EJS 必须经 Node.js 运行时(如 Express
res.render())或构建时(Webpack +html-webpack-plugin+ejs-loader)处理 - Pug 文件(
.pug)必须先编译成 HTML,不能直接作为<script type="text/pug"></script>在浏览器运行 - Webpack 场景下,
html-webpack-plugin的template选项支持.ejs、.pug、.hbs,但前提是装对应 loader 并配好 rule - 路径解析走的是构建系统路径(如
./src/templates/header.ejs),不是相对 URL,include('./footer.ejs')里的路径是相对于当前 EJS 文件位置
最易被忽略的一点:模板引擎解决的是「结构复用 + 数据注入」,但它本身不处理样式作用域、事件绑定或状态响应。一个 <button onclick="doX()"></button> 在多次 compile().call() 后可能绑了多次事件,或因节点重绘丢失监听——这得靠组件化逻辑兜底,不是模板引擎的职责。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











