template.content.cloneNode(true) 是递归渲染的起点而非终点,需手动遍历数据并逐层克隆注入,且仅限 Shadow DOM 中 slot 才生效,匹配需字符串级精确、仅作用于直系子节点。

template.content.cloneNode(true) 是递归渲染的起点,不是终点
原生 <template> 标签本身不支持递归展开,它只是静态片段容器。你写 <template><template>...</template></template>,浏览器只当它是嵌套文本节点,不会自动解析子 <template> 或执行任何分发逻辑。
必须手动遍历数据结构,对每一层调用 cloneNode(true) 创建新副本,再把子级内容注入到占位节点(比如 data-slot="children")中。否则克隆出来的 fragment 里仍带着未实例化的 <template> 标签,不是真实 DOM。
- 每次
cloneNode(true)都是深拷贝,大数据量树形结构容易卡顿,建议加节流或虚拟滚动 -
template.content只能用一次——首次appendChild()后它就为空了,重复使用必须每次都cloneNode(true) - 不要用
innerHTML拼接动态数据,XSS 风险高;所有赋值操作(如img.src、textContent)必须落在克隆副本上
slot 在多层 template 中完全无效,除非桥接到 Shadow DOM
<slot> 不是通用占位符,它只在自定义元素的 Shadow DOM 中生效。放在普通 <template> 里,它就是个无意义的标签名,浏览器既不识别也不分发内容。
常见错误:直接写 <template id="item"><div><slot name="child"></slot></div></template>,然后试图用 JS 插入到页面——<slot> 不会做任何事,也不会报错。
立即学习“前端免费学习笔记(深入)”;
- 要让
<slot>工作,必须注册自定义元素,并在constructor中调用this.attachShadow({ mode: 'open' }) - 模板内容需通过
shadowRoot.appendChild(tpl.content.cloneNode(true))注入,之后用户写的<my-list><span slot="child">xxx</span></my-list>才能匹配 -
<slot>只对自定义元素的**直系子节点**分发,嵌套层级中的节点不会被识别
具名插槽 name 属性必须字符串级精确匹配
插槽内容“消失”但控制台无报错,大概率是 name 匹配失败。原生 Web Components 对 slot 和 <slot name="..."> 的匹配是纯字符串比对,大小写、空格、连字符全算数。
例如:<slot name="header"></slot> 不会接收 <h1 slot="Header">标题</h1>,也不会接收 <div slot="header "></div>(末尾空格)。
- 多个同名插槽节点只渲染第一个,第二个及之后的会被忽略
- fallback 内容(
<slot name="aside">默认侧边栏</slot>)仅在**完全没有**匹配节点时才显示;哪怕有一个空节点(<div slot="aside"></div>),fallback 也不会出现 - 原生不支持动态
slot值,<h2 :slot="sectionName">或<h2 slot="{{name}}">全部无效;真要动态,得用 JS 手动element.setAttribute('slot', name)
递归 + 插槽组合时最容易被忽略的隔离点
真正难的不是写递归函数,而是确保每一层都独立维护自己的 Shadow DOM 上下文和 slot 分发边界。父组件的 <slot> 不会自动穿透到子组件内部,子组件的 slot 也不会向上回传内容。
如果你在递归渲染树形菜单时发现某一层的插槽内容没出来,先确认:这一层是不是用了自定义元素?有没有调用 attachShadow?模板是不是 clone 后 append 到 shadowRoot 而不是 document.body?用户写的 slot="xxx" 节点是不是它的**直系子节点**?
漏掉其中任意一环,slot 就静默失效,且没有任何提示。



















