<template> 标签不渲染且需 JavaScript 激活:其 content 是单次使用的 DocumentFragment,必须用 cloneNode(true) 复制后插入;内部脚本不执行、资源不加载,数据绑定和逻辑需手动处理。

template 标签本身不渲染,但必须用 JavaScript 激活才能生效
<template> 是浏览器原生支持的惰性容器,它内部的 HTML 不会解析、不触发脚本、不加载资源(如 <img>)、也不参与 DOM 树。但它不是“注释”或“字符串”,而是一个真实存在的 HTMLTemplateElement 节点——这意味着你不能直接把它 innerHTML 进去,也不能靠 CSS 选中它里面的内容。
真正让它“活过来”的唯一方式是:取出它的 content 属性(一个 DocumentFragment),再用 cloneNode(true) 复制一份,最后挂载到目标位置。
- 漏掉
.content直接appendChild(template)→ 什么都不会显示,控制台无报错,但 DOM 中看不到内容 - 直接修改
template.content后再插入 → 会移动原始 fragment,下次再用就为空了(DocumentFragment是“单次消耗品”) - 用
innerHTML替代content→ 会丢失 script、style 和表单控件状态,且无法保留原始节点引用
动态激活时必须 cloneNode(true),否则第二次就失效
这是最常踩的坑:template.content 是一个 DocumentFragment,它只能被插入一次。一旦你把它 appendChild() 到某个父节点,它就从 fragment 中被移出,再次调用 appendChild(template.content) 就等于插入一个空 fragment。
正确做法永远是复制:
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
const tmpl = document.querySelector('#my-template');
const instance = tmpl.content.cloneNode(true); // ✅ 必须 true,否则不复制子节点
parent.appendChild(instance);
-
cloneNode(false)只克隆<template>自身,不包含内部结构 - 如果模板里有
<input>或<select>,不cloneNode(true)就没有初始值和事件绑定上下文 - 想反复生成多个实例?每次都要重新
cloneNode(true),不能缓存tmpl.content直接复用
template 内部脚本不会执行,需手动处理逻辑
<template> 中的 <script> 标签会被解析为节点,但**完全不会执行**;同理,<style> 也不会生效,图片不会加载,src/href 不发起请求。这是设计使然,也是它“惰性”的核心体现。
如果你依赖脚本初始化(比如给按钮绑 click、渲染图表),得在 clone 后手动补上:
const instance = tmpl.content.cloneNode(true);
// 手动查找并绑定
instance.querySelector('button').addEventListener('click', handler);
// 或执行内联 script(需提取并 eval,谨慎!)
Array.from(instance.querySelectorAll('script')).forEach(script => {
if (!script.src) {
const fn = new Function(script.textContent);
fn();
}
});
parent.appendChild(instance);
- 避免在
<template>里写<script>,优先把逻辑抽到外部函数中 - 如果用框架(如 Vue/React),它们的 template 编译机制和原生
<template>无关,别混淆 - 动态插入后,CSS 作用域仍受当前文档影响,
<style>需提前注入或用 scoped 方式管理
与 innerHTML 相比,template 更安全、更语义化,但不自动解析属性
用 innerHTML = str 动态插入 HTML 容易 XSS,也绕过 HTML 解析器的校验;而 <template> 是声明式、预定义、可复用的 DOM 片段,天然规避拼接字符串的风险。
但注意:它不会帮你解析数据绑定语法(如 {{name}})、不支持条件或循环——那些都得你自己用 JS 处理:
<template id="item-tmpl">
<li class="item"><span>{{label}}</span></li>
</template>
// ❌ 下面这行不会自动替换 {{label}}
instance.querySelector('span').textContent = data.label;
- 变量插值、条件渲染、列表映射……这些都得手写 JS 实现,
<template>只提供结构容器 - 若需复杂模板能力,建议搭配轻量库(如
lit-html)而非硬刚原生 - 服务端返回的 HTML 字符串,不要直接塞进
<template>的 innerHTML —— 应该用DOMParser解析后再 append 到 content

















