Ant Design Skeleton 骨架屏组件全面实战指南:占位组合、子组件、语义化样式与主题定制
2026/9/9 19:57:13 网站建设 项目流程

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 = trueparagraph = trueavatar = 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%

也就是说,用户只传avatarparagraph={{ rows: 4 }},剩下的宽度、圆角、行数比例全部由组件内部协调,这正是骨架屏能“像真实内容”的原因。而单个占位行内,Paragraph.tsx 会渲染一个<ul>,每一行是一个<li>,当width为数组时按索引取每行宽度、为单个值时仅作用于最后一行。

完整 API 解析

Skeleton 采用“主组件 + 复合子组件”的结构。Skeleton 顶层复合对象由 index.tsx 导出,在 Skeleton.tsx 上挂载了Skeleton.ButtonSkeleton.AvatarSkeleton.InputSkeleton.ImageSkeleton.Node五个子组件。

以下属性表完整继承自原文档,并附源码补充说明。

共同的 API

下述参数为 Skeleton 及 Avatar、Button、Input、Image、Node 等所有骨架元素共享的 API(源自 sharedProps.zh-CN.md):

参数说明类型默认值版本
active是否展示动画效果booleanfalse-
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是否展示动画效果booleanfalse
avatar是否显示头像占位图boolean | SkeletonAvatarfalse
loading为 true 时显示占位图,反之直接展示子组件boolean-
paragraph是否显示段落占位图boolean | SkeletonParagraphPropstrue
round为 true 时,段落和标题显示圆角booleanfalse
title是否显示标题占位图boolean | SkeletonTitlePropstrue
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是否展示动画效果,只在独立使用头像时有效booleanfalse
shape指定头像的形状circle|squarecircle
size设置头像占位图的大小number |large|medium|smallmedium

skeleton主组件内部的头像是不再接收active的(类型见 Skeleton.tsx 中type SkeletonAvatarProps = Omit<AvatarProps, 'active'>),所以“动画开关只对独立使用的Skeleton.Avatar生效”这一特性来自类型层的约束。

Skeleton.Button

属性说明类型默认值版本
active是否展示动画效果booleanfalse-
block将按钮宽度调整为其父宽度的选项booleanfalse4.17.0
shape指定按钮的形状circle|round|square|default--
size设置按钮的大小large|medium|smallmedium-

Skeleton.Input

属性说明类型默认值
active是否展示动画效果booleanfalse
size设置输入框的大小large|medium|smallmedium

值得注意的版本细节:源码中 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;

loadingtrue(或完全未受控)时渲染骨架;loadingfalse时直接把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 的封装,核心规则如下:

  • sizenumber时,直接输出等宽等高的方块,并同步设置lineHeight(见 Element.tsx);
  • sizelarge/small时追加-lg/-sm修饰类,分别对应设计系统中的controlHeightLGcontrolHeightSM
  • shapecircle/square/round时追加对应修饰类,其中circleborderRadius: '50%'实现。

因此默认尺寸的“长相”取决于 antd 的控件高度体系,而不是随意的一块灰,这保证了骨架元素与其对应的真实控件(按钮、输入框)视觉尺寸一致,替换时不产生“胖瘦突变”。尺寸上下文同样受 ConfigProvider 的componentSize约束——子组件在合并尺寸时调用了useSize(见 Avatar.tsx、Button.tsx、Input.tsx)。

另外两个专用元素的说明:

  • Skeleton.Image:内部复用Skeleton.Node,渲染一张内置的图片占位 SVG(见 Image.tsx),支持activesize传入等 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.AvatarSkeleton.ButtonSkeleton.InputSkeleton.ImageSkeleton.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 的colorFillContentcolorFilltitleHeight默认controlHeight / 2paragraphLiHeight同样默认controlHeight / 2blockRadius默认取borderRadiusSM(见 style/index.ts)。另外注意colorcolorGradientEnd两个旧 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-hiddenfocusable="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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询