slot标签仅在shadow dom或vue等框架编译系统中生效,普通html中直接使用会被浏览器忽略;其生效需满足三条件:自定义元素中调用attachshadow、含slot的模板插入影子根、外部内容为直系子节点。

slot 标签本身在普通 HTML 中什么也不做——它只在两种明确上下文中生效:Web Components 的 Shadow DOM,或 Vue(及部分其他框架)的组件编译系统。直接写 <slot></slot> 到页面里,浏览器会把它当普通未知标签忽略,连解析错误都不会报。
Shadow DOM 里的 slot 怎么才能生效
必须满足三个硬性条件,缺一不可:
- 自定义元素类中调用
this.attachShadow({ mode: 'open' })创建影子根 - 把含
<slot></slot>的模板(比如<template><slot name="header"></slot></template>)显式插入到该影子根中,不能只靠 innerHTML 字符串拼接(需用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>❌——中间多了一层<div>,内容不会投射进 <code>slot常见错误是写了
<slot></slot>却没 attachShadow,或者把 slot 放在 light DOM 里试图“手动触发”分发——Shadow DOM 的内容分发是声明式、自动且不可干预的,没有“手动触发”这回事。Vue 中的
<slot></slot>和原生slot完全无关Vue 的
<slot></slot>是编译时语法糖,由 Vue 运行时接管并重写渲染逻辑,和 Shadow DOM 零关系。它的行为完全取决于 Vue 版本:- Vue 2 中用
slot属性(如<p slot="header"></p>)或slot-scope;Vue 3 统一为v-slot指令(简写#),且废弃了作用域插槽的旧写法 - 具名插槽必须严格匹配
name值,大小写敏感;v-slot:default等价于不写v-slot,但不能混用(比如同时写v-slot和v-slot:default会导致编译警告) - 作用域插槽的数据传递靠
v-slot="{ item, index }"解构,这些变量名由子组件通过<slot :item="xxx" :index="i"></slot>显式暴露,不是自动继承父作用域
一个典型陷阱:在 Vue 中误以为
slot能穿透嵌套组件——它只能跨一级父子通信。如果 A 组件用了 B 组件,B 又用了 C 组件,A 无法直接向 C 的<slot></slot>传内容,必须由 B 显式透传(即 B 的模板里也要写<slot></slot>并转发属性)。::slotted()伪类为什么样式不生效这是 Shadow DOM 内部样式作用域的常见困惑点。关键限制有二:
-
::slotted(*)只能作用于**被投射进来的顶层节点**,不能深入后代。例如<my-card><div><p>Text</p></div></my-card>中,::slotted(div)有效,但::slotted(p)无效——<p></p>不是直系投射节点 -
::slotted()不支持复合选择器,不能写::slotted(.class:hover)或::slotted(div > p);仅支持单标签、单类名、单属性等基础选择器 - 若想控制深层样式,得让投射内容自己带 class,或改用 CSS 自定义属性(
--my-card-header-color)配合:host向下传递
最容易被忽略的是:哪怕你正确 attachShadow 且内容直系,如果 shadowRoot 的
mode设为'closed',外部内容根本不会被投射进来——::slotted自然也无从谈起。 - Vue 2 中用











