slot标签仅在Shadow DOM中生效,需满足三个条件:自定义元素调用attachShadow、slot位于Shadow Root内、外部内容为直系子节点;具名插槽name严格区分大小写;默认插槽唯一且无name;name不支持动态绑定。

slot 标签本身不渲染,只在 Shadow DOM 中生效
直接在普通 HTML 文件里写 <slot></slot> 或 <slot name="header"></slot>,页面上什么都不会出现——浏览器把它当未知标签忽略,不报错也不警告。它不是占位符,也不是模板语法糖,只有满足三个硬性条件才会工作:
- 自定义元素类中调用
this.attachShadow({ mode: 'open' }) -
<slot>必须出现在 Shadow Root 内部的模板中(比如通过shadowRoot.appendChild(template.content.cloneNode(true))插入) - 外部传入的内容必须是该自定义元素的**直系子节点**,例如:
<my-card><h2 slot="header">Title</h2></my-card>✅;而<my-card><div><h2 slot="header">Title</h2></div></my-card>❌
具名插槽的 name 属性严格区分大小写
匹配失败八成是因为大小写不对。浏览器执行的是精确字符串比对,不自动转小写、不 fallback、不模糊匹配:
-
<slot name="Header"></slot>只接收<h2 slot="Header"></h2> -
<h2 slot="header"></h2>会被丢进默认插槽(如果存在),或静默丢弃(如果没定义默认插槽) - 主流浏览器(Chrome/Firefox/Safari/Edge)行为完全一致,这不是 bug,是 HTML 规范强制要求
默认插槽只能有一个,且不能带 name
一个组件内可以有多个具名插槽,但最多只能有一个无 name 属性的 <slot></slot>。它的作用很明确:收容所有未被具名插槽匹配的直系子节点。
- 比如子组件含
<slot name="header"></slot>、<slot></slot>、<slot name="footer"></slot>,那么:<h2 slot="header"></h2>→ 进第一个插槽<p slot="footer"></p>→ 进第三个插槽<p>正文</p>或<span slot="unknown"></span>→ 全部进中间那个默认插槽 - 重复声明同名插槽(如两个
<slot name="item"></slot>)允许,但内容只被第一个捕获,后续同名插槽保持空 - 没声明任何
<slot>?所有传入内容都会被丢弃,DOM 中不可见也不可访问
name 不支持动态绑定,只能是静态字面量
name 是纯 HTML 属性,不是响应式字段。写成 <slot :name="headerName"></slot> 或 <slot name="{{ headerName }}"></slot> 都无效——解析阶段就当作字面量处理。
立即学习“前端免费学习笔记(深入)”;
- Vue 模板中若没用
v-bind:name,生成的 DOM 就是<slot name="headerName"></slot>(引号里真的是这 11 个字母) - 真正有效的只有编译/运行时已确定的字面量:
<slot name="header"></slot>、<slot name="user-avatar"></slot> - 想实现“动态插槽名”,得靠 JS 手动控制:比如根据条件渲染多个固定
<slot name="a"></slot>/<slot name="b"></slot>,再配合v-if切换显隐
最容易被忽略的点是:slot 分发不触发重排重绘,也不抛异常,错配就静默丢弃。调试时别只盯着渲染结果,要立刻查 slotElement.assignedNodes() 返回是否为空——但注意,必须等 slotchange 事件触发后调用才可靠,connectedCallback 里直接查大概率是空数组。



















