Ant Design Skeleton 骨架屏组件全面实战指南:占位组合、子组件、语义化样式与主题定制
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design
Skeleton(骨架屏)是 Ant Design 反馈类组件中用于“加载占位”的图形组合:在网络请求尚未返回时,先用与真实内容轮廓近似的灰色块铺满界面,再在数据就绪后无缝替换为真实内容。本文以 components/skeleton/index.zh-CN.md 文档为骨架,结合仓库内 Skeleton.tsx、Element.tsx、Paragraph.tsx 等源码与 demo,系统讲解骨架屏的适用场景、组合式 API、loading 切换、独立子元素(头像/按钮/输入框/图像/自定义节点)、6.0.0 新增的语义化 class/styles 以及主题 Token 定制,帮助你写出信息密度与真实页面轮廓高度一致、可平滑过渡的加载体验。
何时使用 Skeleton
文档给出的判据非常明确,骨架屏适合在以下场景出现:
- 网络较慢、需要长时间等待加载处理时,用骨架屏避免白屏闪烁;
- 图文信息内容较多的列表 / 卡片中,用占位块勾勒出“即将出现的内容形状”;
- 只在第一次加载数据时使用——内容已在手时再展示占位只会制造无谓的跳动;
- 它可以被 Spin 完全替代,但在可用场景下能提供更好的视觉效果和用户体验。二者的定位差异在于:Spin 只表达“正在转圈”,而 Skeleton 通过还原真实布局(标题行、段落行、头像)让用户对页面结构先有预期,从而显著降低等待焦虑。
快速上手:默认骨架与复杂组合
最基础的用法不传任何 props,直接渲染一个默认骨架:
import React from 'react'; import { Skeleton } from 'antd'; const App: React.FC = () => <Skeleton />;该示例对应 demo/basic.tsx。默认输出由标题 + 两行段落组成(源码中title = true、paragraph = true、avatar = false,见 Skeleton.tsx)。
当页面是“头像 + 多行正文”的典型卡片形态时,采用复杂组合:
import React from 'react'; import { Skeleton } from 'antd'; const App: React.FC = () => <Skeleton avatar paragraph={{ rows: 4 }} />;该示例对应 demo/complex.tsx:开启avatar在左侧渲染头像占位,同时用paragraph={{ rows: 4 }}把正文段落扩到 4 行。注意这里并未修改title,标题占位仍默认展示。
源码层面:组合是如何“自动排版”的?
结合 Skeleton.tsx 的实现,可以看到组件会根据「是否有头像、是否有标题、是否有段落」自动推导每个占位块的默认形态,从而保证组合结果符合真实内容比例:
getAvatarBasicProps:仅有标题、无段落时返回{ size: 'large', shape: 'square' }(方形大头像);否则返回{ size: 'large', shape: 'circle' };getTitleBasicProps:无头像但有段落时标题宽度38%;有头像且有段落时标题宽度50%;getParagraphBasicProps:默认 2 行,若只有标题则 3 行;无头像或无标题时最后一行宽61%。
也就是说,用户只传avatar、paragraph={{ rows: 4 }},剩下的宽度、圆角、行数比例全部由组件内部协调,这正是骨架屏能“像真实内容”的原因。而单个占位行内,Paragraph.tsx 会渲染一个<ul>,每一行是一个<li>,当width为数组时按索引取每行宽度、为单个值时仅作用于最后一行。
完整 API 解析
Skeleton 采用“主组件 + 复合子组件”的结构。Skeleton 顶层复合对象由 index.tsx 导出,在 Skeleton.tsx 上挂载了Skeleton.Button、Skeleton.Avatar、Skeleton.Input、Skeleton.Image、Skeleton.Node五个子组件。
以下属性表完整继承自原文档,并附源码补充说明。
共同的 API
下述参数为 Skeleton 及 Avatar、Button、Input、Image、Node 等所有骨架元素共享的 API(源自 sharedProps.zh-CN.md):
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| active | 是否展示动画效果 | boolean | false | - |
| classNames | 用于自定义 Skeleton 组件内部各语义化结构的 class,支持对象或函数 | Record<SemanticDOM, string>|(info: { props }) => Record<SemanticDOM, string> | - | 6.0.0 |
| styles | 用于自定义 Skeleton 组件内部各语义化结构的行内 style,支持对象或函数 | Record<SemanticDOM, CSSProperties>|(info: { props }) => Record<SemanticDOM, CSSProperties> | - | 6.0.0 |
与原文档一致,这两项同时支持 ConfigProvider 级全局配置;
classNames/styles均不参与active动画的默认开启逻辑。具体作用域见下文“Semantic DOM”一节。
Skeleton
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| active | 是否展示动画效果 | boolean | false |
| avatar | 是否显示头像占位图 | boolean | SkeletonAvatar | false |
| loading | 为 true 时显示占位图,反之直接展示子组件 | boolean | - |
| paragraph | 是否显示段落占位图 | boolean | SkeletonParagraphProps | true |
| round | 为 true 时,段落和标题显示圆角 | boolean | false |
| title | 是否显示标题占位图 | boolean | SkeletonTitleProps | true |
SkeletonTitleProps
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| width | 设置标题占位图的宽度 | number | string | - |
宽度最终写入<h3>的行内style.width(见 Title.tsx)。
SkeletonParagraphProps
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| rows | 设置段落占位图的行数 | number | - |
| width | 设置段落占位图的宽度。为数组时为对应的每行宽度,反之则是最后一行的宽度 | number | string | Array<number | string> | - |
例如想要“第一行最宽、第二行 60%、第三行 40%”的效果:
<Skeleton paragraph={{ rows: 3, width: ['100%', '60%', '40%'] }} title={false} />Skeleton.Avatar
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| active | 是否展示动画效果,只在独立使用头像时有效 | boolean | false |
| shape | 指定头像的形状 | circle|square | circle |
| size | 设置头像占位图的大小 | number |large|medium|small | medium |
skeleton主组件内部的头像是不再接收active的(类型见 Skeleton.tsx 中type SkeletonAvatarProps = Omit<AvatarProps, 'active'>),所以“动画开关只对独立使用的Skeleton.Avatar生效”这一特性来自类型层的约束。
Skeleton.Button
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| active | 是否展示动画效果 | boolean | false | - |
| block | 将按钮宽度调整为其父宽度的选项 | boolean | false | 4.17.0 |
| shape | 指定按钮的形状 | circle|round|square|default | - | - |
| size | 设置按钮的大小 | large|medium|small | medium | - |
Skeleton.Input
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| active | 是否展示动画效果 | boolean | false |
| size | 设置输入框的大小 | large|medium|small | medium |
值得注意的版本细节:源码中 Button/Input/Avatar 的
size在类型层面仍保留了'default'取值,但 Element.tsx 会在开发环境下通过devUseWarning提示size="default"已废弃、请改用size="medium"(计划 v7 移除)。新旧项目迁移时请统一收敛为medium。
用 loading 切换:先占位、后内容
loading是骨架屏与传统 loading 最大的不同点:它不是“另起一个状态”,而是包住真实内容,两态共用一个结构。
import React, { useState } from 'react'; import { Button, Skeleton, Space } from 'antd'; const App: React.FC = () => { const [loading, setLoading] = useState<boolean>(false); const showSkeleton = () => { setLoading(true); setTimeout(() => setLoading(false), 3000); }; return ( <Space vertical style={{ width: '100%' }} size={16}> <Skeleton loading={loading}> <h4 style={{ marginBottom: 16 }}>Ant Design, a design language</h4> <p> We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently. </p> </Skeleton> <Button onClick={showSkeleton} disabled={loading}> Show Skeleton </Button> </Space> ); };该示例对应 demo/children.tsx。底层行为可以精确追溯到 Skeleton.tsx:
if (loading || !('loading' in props)) { // ...渲染各类占位块 } return children ?? null;即loading为true(或完全未受控)时渲染骨架;loading为false时直接把children原样渲染出来。由于骨架与真实内容始终处于同一个组件树位置,切换时不会有“先卸载再挂载”的结构跳变,这比裸用 Spin 更适合“数据就绪即替换”的交互。
仓库的测试也对这一行为做了覆盖(如 components/skeleton/tests/index.test.tsx 中对 loading 切换与快照的断言),可直接参考其用法验证自己的页面。
在列表中组合使用
图文类列表是骨架屏最高频的真实场景之一。官方 demo/list.tsx 展示了“渲染真实 List、仅在 loading 时切换内部内容”的标准写法:
<List ...> <List.Item> <Skeleton loading={loading} active avatar> <List.Item.Meta avatar={<Avatar src={item.avatar} />} title={<a href={item.href}>{item.title}</a>} description={item.description} /> {item.content} </Skeleton> </List.Item> </List>要点:列表数据源可以照常渲染,但每条List.Item内部用<Skeleton loading avatar>包裹真实元数据;加载阶段每条显示头像 + 标题 + 段落骨架,加载完成后骨架自动让位给真实头像、标题与摘要。loading由一个Switch驱动(onChange里取反即可手动模拟“先 loading 后就绪”)。
active 动画
对Skeleton主组件或各独立子元素传入active即可展示呼吸流动的高光动画(见 demo/active.tsx):
<Skeleton active /> <Skeleton.Avatar active /> <Skeleton.Button active size="large" />从样式层看,动画不是 CSS 的 background-position 无限循环:在 style/index.ts 定义了ant-skeleton-loading关键帧,占位块背景为三段渐变linear-gradient(90deg, fromColor 25%, toColor 37%, fromColor 63%),通过backgroundSize: '400% 100%'配合 1.4s 的位移动画形成从左到右扫过的流光效果;同时这些动效规则会一次性命中标题、段落、头像、按钮、输入框与图片等全部占位块。
子元素占位:Skeleton.Avatar / Button / Input / Image / Node
当页面只需要“一小块形状”而不是整块骨架时,可直接使用独立元素组件。官方 demo/element.tsx 完整演示了大小、形状、block 与动画的组合操控:
<Skeleton.Button active={active} size={size} shape={buttonShape} block={block} /> <Skeleton.Avatar active={active} size={size} shape={avatarShape} /> <Skeleton.Input active={active} size={size} /> <Skeleton.Button active={active} size={size} shape={buttonShape} block={block} /> <Skeleton.Input active={active} size={size} block={block} /> <Skeleton.Image active={active} /> <Skeleton.Node active={active} style={{ width: 160 }} /> <Skeleton.Node active={active}> <DotChartOutlined style={{ fontSize: 40, color: '#bfbfbf' }} /> </Skeleton.Node>尺寸与形状如何落到样式上?这些子组件本质都是对底层 Element.tsx 的封装,核心规则如下:
size为number时,直接输出等宽等高的方块,并同步设置lineHeight(见 Element.tsx);size为large/small时追加-lg/-sm修饰类,分别对应设计系统中的controlHeightLG、controlHeightSM;shape为circle/square/round时追加对应修饰类,其中circle由borderRadius: '50%'实现。
因此默认尺寸的“长相”取决于 antd 的控件高度体系,而不是随意的一块灰,这保证了骨架元素与其对应的真实控件(按钮、输入框)视觉尺寸一致,替换时不产生“胖瘦突变”。尺寸上下文同样受 ConfigProvider 的componentSize约束——子组件在合并尺寸时调用了useSize(见 Avatar.tsx、Button.tsx、Input.tsx)。
另外两个专用元素的说明:
- Skeleton.Image:内部复用
Skeleton.Node,渲染一张内置的图片占位 SVG(见 Image.tsx),支持active、size传入等 Node 行为; - Skeleton.Node:通用的自定义节点容器(Node.tsx),可放入任意图标或内容,最适合“图标型加载占位”。
Semantic DOM:语义化 class 与 style 定制(6.0.0)
6.0.0 起,Skeleton 可以通过classNames/styles(对象或接收{ props }的函数)针对语义化 DOM 节点做细粒度定制,而不是面向易碎的层级选择器。这与仓库引入的useMergeSemantic语义化体系一致(见 Skeleton.tsx 的SkeletonSemanticType定义)。
Skeleton 的语义节点
由 demo/_semantic.tsx 可确认 Skeleton 暴露以下 6 个语义结构:
| 语义 key | 含义 |
|---|---|
| root | 根元素,承载表格布局、宽度、动画与圆角等容器基础样式 |
| header | 头部区域,放置头像占位(table-cell + 内边距布局) |
| section | 内容区块,承载标题与段落的整体布局 |
| avatar | 头像占位块本身 |
| title | 标题占位块 |
| paragraph | 段落占位块(作用于<ul>容器) |
子元素(Element)的语义节点
对Skeleton.Avatar、Skeleton.Button、Skeleton.Input、Skeleton.Image、Skeleton.Node而言,语义结构简化为两层(见 demo/_semantic_element.tsx 与 Element.tsx 类型定义):root(外层容器 div)与content(实际可见的灰块 span)。
实战:class 与 styles 的“对象 + 函数”两种写法
官方 demo/style-class.tsx 演示了两种形态的完整用法。对象形态——把不同的语义 key 指向各自样式:
const styles: SkeletonProps['styles'] = { avatar: { border: '1px solid #aaa' }, title: { border: '1px solid #aaa' }, };函数形态——根据info.props(例如是否active)动态决定样式,实现“仅在动画开启时加描边/换色”的分支逻辑:
const stylesFn: SkeletonProps['styles'] = (info): GetProp<SkeletonProps, 'styles', 'Return'> => { if (info.props.active) { return { root: { border: '1px solid rgba(229, 243, 254, 0.3)' }, title: { backgroundColor: 'rgba(229, 243, 254, 0.5)', height: 20, borderRadius: 20 }, }; } return {}; };而细粒度“段落内部每一行<li>”的样式,需要通过classNames+ CSS-in-JS 后代规则实现(demo 中用& > li命中段落占位行)。结合 style-class.md 可确认该能力自 6.0.0 起可用;在引入 classNames/styles 时也需要注意其合并优先级,Skeleton.tsx 注释给出了一条经验链:contextClassNames.root < contextClassName < componentClassNames.root < componentClassName < rootClassName。
主题变量(Design Token)定制
Skeleton 的全部可定制 Token 由 style/index.ts 声明,常用的有:
| Token | 说明 |
|---|---|
| gradientFromColor | 渐变色起点颜色(背景主色) |
| gradientToColor | 渐变色终点颜色(高光) |
| titleHeight | 标题骨架屏高度 |
| blockRadius | 骨架屏圆角 |
| paragraphMarginTop | 段落骨架屏上间距 |
| paragraphLiHeight | 段落骨架屏单行高度 |
其中gradientFromColor/gradientToColor的默认值分别取自全局 Token 的colorFillContent与colorFill;titleHeight默认controlHeight / 2,paragraphLiHeight同样默认controlHeight / 2,blockRadius默认取borderRadiusSM(见 style/index.ts)。另外注意color与colorGradientEnd两个旧 Token 已标记废弃,映射关系见该文件的deprecatedTokens配置。
通过 ConfigProvider 的theme.components.Skeleton覆盖即可全局生效:
import React from 'react'; import { ConfigProvider, Skeleton } from 'antd'; const App: React.FC = () => ( <ConfigProvider theme={{ components: { Skeleton: { blockRadius: 30, titleHeight: 50, gradientFromColor: '#222', gradientToColor: '#444', paragraphMarginTop: 30, paragraphLiHeight: 30, }, }, }} > <Skeleton loading active /> </ConfigProvider> );该示例对应 demo/componentToken.tsx,可据此快速制作深色底、大圆角的“品牌化骨架屏”。
无障碍与质量保障
骨架屏本质是装饰性占位,测试上除了常规的渲染/切换断言外,仓库还专门维护了可访问性与图片语义测试:a11y.test.ts 校验骨架屏在自动化无障碍检查下无违规项;image.test.ts 关注Skeleton.Image的内置 SVG 已设置aria-hidden与focusable="false",避免占位图片干扰读屏。在自行组合骨架屏时也建议遵循同样原则:占位图形不作为可聚焦元素、不给真实内容的可访问名称造成噪音。
小结
Skeleton 是 antd 中实现“渐进式加载反馈”的主力组件:用<Skeleton>包裹真实内容、以loading双态切换,是最推荐的用法;用avatar/paragraph/title的对象式配置即可还原头像卡片、图文列表等复杂轮廓,这些组合比例在源码层由 Skeleton.tsx 内部函数自动协调;局部占位则由Skeleton.Avatar/Button/Input/Image/Node独立承担。若需深度定制外观,6.0.0 的classNames/styles语义化节点与theme.components.Skeleton的 Design Token 双通道,足以覆盖从“改一个圆角”到“整套品牌化骨架”的全部诉求。组件全部源码位于 components/skeleton,docs 与 demo 一应俱全,可直接对照查阅。
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考