不是动态挂载机制,仅在shadow dom内做内容投影;真正实现动态挂载的是customelements.define()配合this.attachshadow(),且必须满足直系子节点、正确插入shadow root、严格大小写与空格敏感等前提。

<slot></slot> 不是动态组件的挂载机制,它不负责“挂载”,只做内容投影;真正实现动态挂载的是 customElements.define() + this.attachShadow() 的组合,<slot></slot> 只在 Shadow DOM 内起分发作用。
为什么直接写 <template><slot></slot></template> 没反应
因为 <template></template> 本身不渲染,<slot></slot> 也不自动激活——二者没有绑定关系。浏览器把这段当纯声明,既不解析 <slot></slot>,也不尝试分发任何内容。
- 必须用 JavaScript 显式读取
<template></template>的.content,再cloneNode(true)插入到 Shadow Root 中 - 若漏掉
this.attachShadow({ mode: 'open' }),<slot></slot>就是死标签,连 fallback 都不会显示(除非你把它当普通 HTML 写在 light DOM) - 常见误操作:把
<template id="tpl"><slot>fallback</slot></template>放在页面任意位置,然后期望<my-comp><p>hello</p></my-comp>自动生效 → 不会
<slot></slot> 分发失败的三个硬性条件检查点
插槽内容“消失”不是 bug,而是未满足 Web Components 基础前提。逐项确认:
- 自定义元素构造函数中是否调用了
this.attachShadow({ mode: 'open' })?重复调用会报错,不调用则整个影子根不存在 - 传入的内容是否为该自定义元素的**直系子节点**?
<my-comp><div><p slot="main">no</p></div></my-comp>中的<p></p>不会被分发,因被<div> 隔开 <li> <code><slot name="main"></slot>是否严格出现在 Shadow DOM 内?例如写在this.shadowRoot.innerHTML = '<div><slot name="main"></slot></div>'或通过appendChild(tpl.content.cloneNode(true))插入 - 在自定义元素内部执行:
this.shadowRoot.querySelector('slot[name="header"]').assignedNodes(),返回空数组说明没匹配上 - 注意:换行符、空格、文本节点都会被计入
assignedNodes()结果,所以<my-comp></my-comp>(含空格)也会让 fallback 不显示 - 若需过滤纯文本,改用
.assignedElements(),它只返回 Element 节点 - 监听变更要用
slotchange事件:slot.addEventListener('slotchange', () => console.log('re-projected')),而非 MutationObserver - Web Components 中:父组件改了传入的 light DOM 节点(比如
el.textContent = 'new'),插槽内文本会变,但这是 DOM 属性更新,不是插槽重执行 - 若想响应式更新结构,必须手动操作:监听
slotchange后调用this.render()(如果你封装了 render 方法),或重新设置shadowRoot.innerHTML - Vue 场景下同理:
<my-comp>{{ msg }}</my-comp>不会随msg变化而更新,必须包一层<template v-slot:default>{{ msg }}</template>让 Vue 纳入响应链
如何验证插槽是否真被分配了内容
别依赖肉眼观察或控制台 Elements 面板——投影后节点仍保留在 light DOM,Shadow DOM 里只留占位。正确方式是查 API:
动态内容更新时插槽不刷新?这不是插槽的问题
<slot></slot> 是静态投影机制,不监听数据变化。所谓“动态”,靠的是外部触发重渲染,而非插槽自身响应:
真正卡住人的永远不是怎么写 <slot></slot>,而是没意识到它只在 Shadow DOM 生效、只认直系子节点、且对空格和大小写零容忍——这些限制不报错,只静默丢弃内容。











