应使用 template.content.cloneNode(true) 克隆模板内容,因其安全、兼容性好且不干扰原模板;appendChild 直接操作 会静默失败,template.children 恒为空,importNode 仅适用于跨文档场景。

template.content 不能直接 appendChild
直接对 <template></template> 元素调用 appendChild() 会静默失败,因为它的 content 是一个 DocumentFragment,不是普通节点。浏览器不允许把 fragment 当作子节点插入(除非先克隆或导入)。
常见错误现象:document.querySelector('template').appendChild(node) 报错或无反应;或者误以为 template.children 能取到内容——其实它永远是空的。
-
template.content是只读的 DocumentFragment,必须用它来克隆或导入 - 不要试图遍历
template.childNodes后再手动 append,fragment 内部节点没有父引用,插入时会自动脱离 - 若 template 内含
<script></script>或<style></style>,它们在克隆后默认不执行/不生效,需手动处理
cloneNode(true) 是最常用、最安全的克隆方式
cloneNode(true) 作用于 template.content,能完整复制所有子节点、属性、文本,且兼容性好(IE9+),无需额外导入步骤。
使用场景:动态渲染列表项、表单模板、弹窗内容等需要多次复用的结构。
立即学习“前端免费学习笔记(深入)”;
<template id="item-tpl">
<li class="item"><span class="name"></span></li>
</template>
<script>
const tpl = document.getElementById('item-tpl');
const frag = tpl.content.cloneNode(true); // ✅ 正确
const li = frag.querySelector('li');
li.querySelector('.name').textContent = 'foo';
document.getElementById('list').appendChild(frag);
</script>
-
cloneNode(true)复制的是content的副本,原 template 不受影响 - 克隆后的节点已脱离文档,可自由修改 class、text、事件绑定,再插入目标容器
- 注意:克隆不会触发
connectedCallback(自定义元素),也不会重跑内联 script
importNode 是跨文档场景才需要的冷门方案
importNode() 主要用于 iframe 或不同 document 实例之间迁移节点,比如从 iframe 的 template 中取内容到主页面。在单页应用中几乎用不到,强行用反而多此一举。
容易踩的坑:document.importNode(template.content, true) 看似可行,但 template 本身属于当前 document,import 属于冗余操作,且某些旧版 Safari 对 fragment import 支持不稳定。
- 仅当节点来自另一个 document(如
iframe.contentDocument)时,才必须用importNode -
importNode第二个参数为true才深拷贝;false只拷顶层 fragment,内部节点仍被引用 - 不要混用:
importNode(template.content, true)+appendChild和cloneNode效果一致,但多了兼容性和语义负担
克隆后记得清空 template 或管理状态
很多人忽略 template 本身是静态声明,但若模板里含带 value 的 <input>、checked 的 <radio></radio> 或内联 style,多次克隆会导致状态残留(比如上一次填的值还在)。
这不是克隆方法的问题,而是模板设计问题。
- 模板中尽量避免写死
value、checked、selected,改用 JS 动态赋值 - 如果必须初始化,克隆后立即重置:
input.value = ''、checkbox.checked = false - 复杂组件建议封装成函数,每次调用都返回新实例,而不是反复操作同一 fragment
template 自身的 DOM 属性或子树,只动 content,且优先选 cloneNode(true)。importNode 是边界 case,日常开发里写它大概率说明你误解了 document 上下文。



















