template元素默认不渲染,必须手动克隆后插入DOM;直接使用template.content会因“移动式引用”导致二次appendChild报错,正确做法是每次调用cloneNode(true);含<script>需手动执行,含自定义元素须确保已定义或调用upgrade。

template元素默认不渲染,必须手动克隆后插入DOM
template 是浏览器原生的“内容占位符”,它的子节点在解析时不会被激活、不参与渲染、不执行脚本、不加载资源(比如 <img> 的 src 不会请求)。直接用 document.querySelector('template').content 只拿到一个 DocumentFragment,它还不能直接挂到页面上——得先克隆,再插入。
常见错误是写成:el.appendChild(template.content),这会导致第二次调用时报错 Failed to execute 'appendChild' on 'Node': The node to be inserted is a document fragment that is already in the document,因为 content 是“移动式引用”,只能用一次。
- ✅ 正确做法:每次都要用
template.content.cloneNode(true) - ⚠️ 注意:如果
template里有<script>,cloneNode(true)不会重新执行它;需要手动eval或创建新script标签 - ? 小技巧:可以给
template加id或data-name方便复用,比如<template id="user-card">
用innerHTML + insertAdjacentHTML绕过template也能快速“激活”
如果你只是想动态插入静态 HTML 字符串,其实不需要 template——直接读取 template.innerHTML,再用 insertAdjacentHTML() 插入更轻量,也规避了 DocumentFragment 生命周期问题。
例如:
立即学习“Java免费学习笔记(深入)”;
const tmpl = document.querySelector('#item-tmpl');
const html = tmpl.innerHTML.replace('{{name}}', 'Alice');
listEl.insertAdjacentHTML('beforeend', html);
这种方式适合纯结构+变量替换场景,但要注意 XSS 风险,别把用户输入直接拼进 innerHTML。
- ✅ 优势:无克隆开销,支持多次复用同一段 HTML 字符串
- ❌ 缺陷:丢失原生
template的语义(如不阻塞解析、不预加载资源),且内联事件(onclick="...")需额外绑定 - ? 若模板含
<slot>或需要后续 JS 控制,还是得走content.cloneNode(true)路线
template中含自定义元素或Web Component时,需确保已定义
如果 template 里写了 <my-button> 这类自定义标签,但对应 customElements.define() 还没执行,克隆后插入 DOM 也不会升级——浏览器只对“首次插入文档”时的标签做升级检查。
所以必须保证:
- 自定义元素定义早于
template激活逻辑(通常放在<head>或模块顶层) - 或者手动触发升级:
customElements.upgrade(fragment),传入克隆后的DocumentFragment - 若用 Shadow DOM,记得在克隆后对每个子组件调用
attachShadow(如果它没自动处理)
漏掉定义或升级,结果就是页面上只显示空标签,控制台无报错,极难排查。
避免在循环中反复 querySelector template
高频操作(如渲染列表)中,每次都在循环里写 document.querySelector('#tmpl'),既低效又容易因 DOM 变动导致查不到。
- ✅ 推荐:启动时缓存一次,
const tmpl = document.querySelector('#tmpl'); - ⚠️ 更安全:加判空,
if (!tmpl) throw new Error('Template #tmpl not found'); - ? 如果模板由构建工具注入(如 Webpack 的
html-loader),注意它可能被包裹进<body>外层,需用document.body.querySelector或提升作用域
真正麻烦的不是语法,而是 template 内容看似“存在”,实则未就绪——比如异步加载的模板片段、动态生成的 ID、服务端渲染遗漏的 template 标签。这些地方一卡,整块 UI 就静默失败。


















