slot 是内容插槽占位符,不渲染为 dom 节点,仅决定外部内容插入位置;本质区别在于它不产生输出,而是由框架或 shadow dom 实现内容投影,需配合组件封装逻辑生效。

slot 是什么,它和普通 HTML 标签有什么本质区别
slot 不是渲染出来的 DOM 节点,而是一个“内容插槽”占位符。它本身不产生任何输出,只决定外部传入的内容该往哪塞。这点常被误当成 div 或 span 来用,结果发现样式失效、事件绑定不上——因为插槽内容实际被移进了组件内部的 Shadow DOM(如果用了 attachShadow)或被 Vue/React 等框架接管了生命周期。
真正起作用的是配合 <slot></slot> 的封装逻辑:父组件写在组件标签内的内容,默认会“投影”到子组件里第一个 <slot></slot> 的位置;有 name 属性的 <slot name="header"></slot>,则只接收 <template slot="header">...</template> 或 <p slot="header">...</p> 这类显式指定的内容。
纯 HTML(无框架)中用 slot 实现骨架组件的关键步骤
原生 Web Components 支持 slot,但必须搭配 customElements.define 和 shadowRoot 才能生效。直接在普通 HTML 里写 <my-card><h2>标题</h2></my-card> 并不会自动触发插槽行为。
使用 PptxGenJS 和 Python 解析,将结构化的 HTML/CSS 幻灯片内容转换为原生、可编辑的 PowerPoint(.pptx)演示文稿。
- 必须用
attachShadow({mode: 'open'})创建影子根,否则slot不解析 - 组件模板中需显式写出
<slot></slot>或带name的具名插槽 - 外部传入的内容必须是合法 HTML 片段,不能是字符串变量(比如
innerHTML = '<slot>xxx</slot>'不会激活插槽机制) - 若想支持 fallback 内容(即没传内容时显示默认结构),写成
<slot><div class="skeleton"></div></slot>
Vue 中使用 slot 时最容易漏掉的两件事
Vue 的 slot 语法糖(如 v-slot:header)掩盖了底层 DOM 投影规则,导致开发者常忽略两个硬性约束:
-
<slot></slot>必须出现在组件的template中,且不能被v-if包裹——否则整个插槽逻辑会被跳过,fallback 内容也不显示 - 具名插槽传递内容时,
<template v-slot:footer></template>必须作为组件的直接子节点;如果套了一层<div>,Vue 就识别不到插槽内容,最终渲染为空 <li>作用域插槽(<code>slot-scope/v-slot:item="{ data }")的数据对象必须由组件内部通过<slot :data="item"></slot>显式传出,不能靠父组件“猜” - 直接透传:
return <article>{props.children}</article>;—— 这才是“插槽”语义 - 避免
{String(props.children)}或{props.children?.toString()},这会丢失 React 元素的类型和事件绑定能力 - 若需多插槽,用对象解构:
const { header, children, footer } = props;,再分别渲染,比模拟slot="header"更直白可靠 - 骨架场景下,推荐把 loading 状态和插槽内容拆成两个分支:
{loading ? <skeleton></skeleton> : children},而不是试图让slot自动感知状态
React 没有 slot,但可以用 children + props 模拟出等效能力
React 没有原生 slot,但 children prop 天然承担了默认插槽角色,而自定义 prop(如 header、footer)可替代具名插槽。关键在于别把 children 当成字符串处理:
真正的复用难点不在语法,而在如何让骨架结构不依赖具体业务字段——比如一个列表组件的 slot 里塞了 <usercard name="{user.name}" avatar="{user.avatar}/"></usercard>,那这个组件就很难复用于订单列表。骨架的本质是“留白”,不是“预设”。










