template.content.clonenode(true)是唯一可靠克隆方式,因其content为只读documentfragment,只能被移动一次,必须深克隆后插入;应在connectedcallback中执行,确保模板就绪且组件已挂载。

<template></template> 不是占位符容器,也不是 Shadow DOM 的语法糖。它本质是一个“惰性文档片段”,只在你调用 cloneNode(true) 时才真正参与 DOM 构建——这点不搞清,所有封装都会出错。
为什么 template.content 只能 clone 一次
template.content 是一个 DocumentFragment,不是普通节点。它只能被 append 或 insert 一次;第二次再用 shadowRoot.appendChild(template.content),会静默失败(内容消失),因为 fragment 已被移走。
- 错误写法:
shadowRoot.appendChild(template.content)—— 多次实例化时第二例就空了 - 正确写法:
shadowRoot.appendChild(template.content.cloneNode(true)) - 别在
constructor里做这事:此时template元素可能还没被 HTML parser 解析出来,document.getElementById()返回null - 推荐时机:在
connectedCallback中执行克隆,确保组件已挂载、模板已就绪
template 里写 slot 但没生效?检查三件事
<slot></slot> 不是字符串替换,它是运行时内容搬运机制——只在模板被克隆进带 Shadow DOM 的元素后才激活。
-
<slot name="header"></slot>的name值必须全小写、无空格、无特殊字符;外部传入时也得严格匹配<my-card><h1 slot="header">标题</h1></my-card> - 默认插槽写成
<slot>默认文案</slot>,否则没传子节点时区域空白且无提示 - 别把
<template></template>套在<div> 里再塞进 shadowRoot:只有 <code>template.content的子节点会被克隆,外层 wrapper 会被丢弃如何让 template 和 Custom Element 真正串起来
关键不在“模板放哪”,而在“谁来克隆、何时克隆、克隆给谁”。很多人直接往
shadowRoot.innerHTML拼字符串,结果逻辑散、样式漏、插槽死。- 模板必须声明在 light DOM 中(比如
底部),不能写在shadowRoot里 - 模板需有明确标识,如
id="card-template",JS 通过document.getElementById('card-template')获取 - 克隆后若需动态填充(比如根据
this.getAttribute('title')设置文本),要在cloneNode(true)后手动操作,例如:cloned.querySelector('.title').textContent = this.title - 避免在
connectedCallback里重复克隆:加个if (!this._rendered)标记,或改用attributeChangedCallback+render()组合更新局部
最常被忽略的一点:template 内的
<style></style>标签会随克隆一起进入 shadowRoot,但它只是普通样式节点,不会自动作用于外部;而外部 CSS 也无法穿透进来——样式隔离是靠 Shadow DOM 实现的,不是靠<template></template>自身。 - 模板必须声明在 light DOM 中(比如











