template.content.cloneNode(true) 是唯一安全入口,因 template 子节点封装在 DocumentFragment 中,innerHTML 永为空,textContent 仅返回纯文本;必须用 cloneNode(true) 深克隆以保留 DOM 状态和嵌套关系。

template.content.cloneNode(true) 是唯一安全入口
很多人卡在第一步:用 document.querySelector('template').innerHTML 拿不到内容,返回空字符串。这不是 bug,而是规范行为——template 的子节点被封装在 DocumentFragment 里,只能通过 content 属性访问。template.innerHTML 永远为空,template.textContent 只返回带缩进的纯文本,无法操作结构。
必须用 template.content.cloneNode(true) 获取可操作副本。不加 true 是浅克隆:事件监听器丢失、input 光标位置清零、表单值重置;加 true 才能保留全部 DOM 状态和嵌套关系。
填值不用 innerHTML,优先用 textContent 和 value
用 innerHTML 填值等于主动引入 XSS 风险,还会销毁已绑定的事件监听器(比如模板里原本有 ,克隆后 onclick 就没了)。
- 纯文本内容:
el.querySelector('.title').textContent = data.title - 表单控件:
el.querySelector('input').value = data.value(不是textContent) - 需保留 HTML 结构?先对用户输入做转义:
text.replace(/&/g, '&').replace(/, ',再塞进innerHTML
批量插入必须走 DocumentFragment
循环里每轮都 container.appendChild(cloned),等于让浏览器反复重排重绘。100 条数据就明显卡顿;500 条基本卡死。
正确做法是:
- 创建
const frag = document.createDocumentFragment() - 循环中
frag.appendChild(cloned) - 最后只调一次
container.appendChild(frag)
DocumentFragment 是轻量中间容器,不触发重排重绘,也不在 DOM 树中可见,性能提升立竿见影。
data-* 属性能保留,slot 不生效,别混用 Web Components
data-id、data-status 这类自定义属性在克隆后完整保留,但不会自动变成 JS 对象属性——想读取还得用 el.getAttribute('data-id')。
slot 相关逻辑完全不生效: 克隆后,slot="title" 根本没被解析,因为 template 没挂载到 Shadow DOM,也没激活 slot 机制。这种写法实际无效,容易误以为“结构对了就行”。
真正容易被忽略的是 hydration 边界:服务端已输出的 DOM 能 hydrate,但 template 克隆出来的部分不会自动绑定事件、不会恢复状态、ID 冲突时 querySelector 会找错元素。这问题不报错,只静默失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











