是结构预定义工具而非shadow dom替代品,用于声明静态结构并克隆插入,需配合clonenode(true)使用,推荐在connectedcallback中执行,支持slot但须正确嵌入shadow dom。

template 标签不是 Shadow DOM 的替代品,而是结构预定义工具
很多人一上来就往 shadowRoot.innerHTML 里硬塞 HTML 字符串,结果模板逻辑散在 JS 里,难维护、易出错、无法复用。真正该用 <template></template> 的地方,是把组件的「静态结构」提前声明好,再由 JS 控制何时、如何克隆插入。
它不渲染、不执行脚本、不加载资源——只存结构。浏览器解析时跳过它,直到你显式调用 content.cloneNode(true) 才真正参与 DOM 构建。
-
<template></template>必须有明确的id或通过document.querySelector获取,否则 JS 拿不到引用 - 不能直接写在
shadowRoot里——它属于 light DOM,得放在页面任意位置(比如底部或组件 JS 同级的<script></script>前) - 模板内可含
<slot></slot>,但插槽行为生效的前提是:该模板内容最终被 append 到带 Shadow DOM 的元素中
如何把 template 和 Custom Element 真正串起来
关键不在“放进去”,而在“什么时候 clone、怎么 clone、clone 给谁”。常见错误是直接 shadowRoot.appendChild(template.content),结果后续多次实例化时内容被移走一次就没了。
正确做法永远是:template.content.cloneNode(true),然后插入到 shadowRoot。
- 必须用
cloneNode(true),原生template.content是“文档片段”,只能被移动一次 - 别在
constructor里操作模板——此时this还没挂载,shadowRoot虽已存在,但外部 HTML 尚未解析完毕,template元素可能还没被 parser 创建出来 - 推荐在
connectedCallback中执行克隆,确保组件已插入文档且模板可用 - 如果模板含动态内容(如需根据属性渲染不同文本),应在克隆后手动设置,而不是靠 innerHTML 拼接
slot 在 template 里写法不对,会导致内容分发失效
<slot></slot> 不是占位符,它是运行时的内容搬运工。它只在模板被克隆进 Shadow DOM 后才起作用;如果模板只是挂在 light DOM 里,<slot></slot> 会被当成普通标签忽略。
- 单个默认插槽写
<slot></slot>,传入子节点自动填充;多个插槽必须加name属性,且外部使用时也要对应<span slot="desc">xxx</span> - 不要在
<template></template>外层再套一层 div 包裹 ——template.content只包含其子节点,外层 wrapper 会被丢弃 -
<slot></slot>的name值区分大小写,且不能含空格或特殊字符,否则匹配失败 - 如果插槽没内容 fallback,记得写默认文本:
<slot>默认提示</slot>,否则空白区域不可见也不报错
template + Shadow DOM 组合下容易被忽略的性能点
看似只是“多了一步 clone”,但实际影响首次渲染时机和内存占用。尤其当组件高频创建(如列表项)时,模板复用不当会放大开销。
- 模板应全局只定义一次,不要每次
connectedCallback都重新querySelector—— 提前缓存const tmpl = document.getElementById('my-btn-tmpl') - 避免在模板里写内联样式或 script;Shadow DOM 内部样式应走
<style></style>标签注入,JS 行为统一由类方法控制 - 若模板结构复杂(含多层嵌套、大量元素),考虑用
document.importNode替代cloneNode(兼容性略差但更语义化) - IE 完全不支持
<template></template>,哪怕加 polyfill 也难以完美模拟其“不渲染”特性,生产环境需降级方案(如用textarea存 HTML 字符串)
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











