ant-design-vue Skeleton 骨架屏组件完全指南:API 详解与源码级实现原理
2026/9/20 11:01:38 网站建设 项目流程

ant-design-vue Skeleton 骨架屏组件完全指南:API 详解与源码级实现原理

【免费下载链接】ant-design-vue🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜项目地址: https://gitcode.com/gh_mirrors/an/ant-design-vue

Skeleton(骨架屏)是 ant-design-vue 中用于「在内容加载完成前展示占位图形」的反馈型组件,能显著缓解用户等待焦虑、降低页面跳动感。本文将围绕 components/skeleton/index.zh-CN.md 官方文档的完整 API 体系展开,并结合仓库源码(Skeleton.tsx、style/index.ts 等)剖析其组合逻辑、默认布局策略、动画实现与样式 Token 机制,帮助你在真实项目中正确选型、精准配置并理解其底层运作方式。

一、组件定位:什么时候该用 Skeleton

根据官方文档,Skeleton 的核心使用场景有四类:

  • 网络较慢、需要长时间等待加载处理的场景,避免页面长时间空白;
  • 图文信息内容较多的列表 / 卡片,用骨架占位勾勒出即将出现的内容轮廓;
  • 仅在第一次加载数据时使用,数据就绪后切换为真实内容;
  • 可以被 Spin 完全替代,但在可用场景下能提供比 Spin 更好的视觉效果和用户体验——Spin 只是一个居中的加载图标,而 Skeleton 通过模拟标题、段落、头像的真实排版结构,让用户提前感知页面布局,减少加载完成时的视觉跳动。

从组件分类看,Skeleton 属于「反馈(Feedback)」类型组件(见文档 frontmatter 中的type: 反馈),与 Spin、Message 等并列,定位是「在需要等待加载内容的位置提供一个占位图形组合」。

二、Skeleton 主组件 API 详解

Skeleton 主组件接收 5 个核心属性,官方文档参数表如下:

属性说明类型默认值
active是否展示动画效果booleanfalse
avatar是否显示头像占位图boolean | SkeletonAvatarPropsfalse
loadingtrue时,显示占位图。反之则直接展示子组件boolean-
paragraph是否显示段落占位图boolean | SkeletonParagraphPropstrue
title是否显示标题占位图boolean | SkeletonTitlePropstrue

2.1 基本用法与默认组合

最简单的用法只需一行代码(见 demo/basic.vue):

<a-skeleton />

不传任何参数时,会渲染出「标题 + 段落」的默认骨架组合。这一行为由 Skeleton.tsx 中的initDefaultProps决定:

props: initDefaultProps(skeletonProps(), { avatar: false, title: true, paragraph: true, }),

即默认avatar=falsetitle=trueparagraph=true,与文档默认值完全一致。

2.2 avatar / title / paragraph:布尔值还是对象

这三个属性既可以是boolean,也可以是各自对应的配置对象。当传入对象时,其内部的配置项会合并到组件自动计算的基础属性之上。核心逻辑见 Skeleton.tsx:

function getComponentProps<T>(prop: T | boolean | undefined): T | {} { if (prop && typeof prop === 'object') { return prop; } return {}; }

getComponentProps负责抽取对象形式的配置;当传入布尔值时返回空对象,则完全采用组件内部的默认布局。

2.3 智能默认布局:根据组合自动推算占位尺寸

Skeleton 并不是简单地堆砌三个占位块,而是会根据「当前显示了哪些部分」自动调整每个部分的默认尺寸,让组合看起来更接近真实排版。这套逻辑在 Skeleton.tsx 中实现:

function getAvatarBasicProps(hasTitle: boolean, hasParagraph: boolean): SkeletonAvatarProps { if (hasTitle && !hasParagraph) { // 只有标题时,头像用方形 return { size: 'large', shape: 'square' }; } return { size: 'large', shape: 'circle' }; } function getTitleBasicProps(hasAvatar: boolean, hasParagraph: boolean): SkeletonTitleProps { if (!hasAvatar && hasParagraph) { return { width: '38%' }; } if (hasAvatar && hasParagraph) { return { width: '50%' }; } return {}; } function getParagraphBasicProps(hasAvatar: boolean, hasTitle: boolean): SkeletonParagraphProps { const basicProps: SkeletonParagraphProps = {}; if (!hasAvatar || !hasTitle) { basicProps.width = '61%'; } if (!hasAvatar && hasTitle) { basicProps.rows = 3; } else { basicProps.rows = 2; } return basicProps; }

可以总结出以下默认策略:

  • 头像(avatar):默认size: 'large';当「有标题但无段落」时自动变为square(方形),其余情况为circle(圆形);
  • 标题(title):有段落无头像时宽度为38%,有头像且有段落时宽度为50%,否则撑满 100%;
  • 段落(paragraph):无头像或无标题时宽度为61%(只作用于最后一行);无头像但有标题时默认 3 行,其余情况默认 2 行。

因此,像官方演示中的「复杂组合」——<a-skeleton avatar :paragraph="{ rows: 4 }" />(见 demo/complex.vue)——只需覆盖paragraph.rows,头像与标题的尺寸、宽度仍由组件自动推导。

2.4 loading:占位与真实内容的一键切换

loading是 Skeleton 与数据流结合的关键开关。为true时渲染占位图;为false时直接渲染slots.default中的真实内容。该分支逻辑见 Skeleton.tsx:

if (loading || props.loading === undefined) { // ... 渲染骨架占位图 } return slots.default?.();

注意一个细节:props.loading === undefined时同样渲染占位图,即未显式传入 loading 时,组件默认处于骨架态,直到你传入loading=false才展示子组件。官方演示 demo/children.vue 给出了典型用法——加载中展示骨架,加载完成后无缝切换为真实内容:

<a-skeleton :loading="loading"> <div> <h4>Ant Design Vue, 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> </div> </a-skeleton> <a-button :disabled="loading" @click="showSkeleton">Show Skeleton</a-button>
const loading = ref<boolean>(false); const showSkeleton = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 3000); };

结合a-list使用时的完整示例可参考 demo/list.vue,在a-list-item内部包裹<a-skeleton :loading="loading" active avatar>,实现列表骨架加载。

2.5 active:加载光效动画

activetrue时,骨架块会呈现从左到右的渐变扫光动画。该动画并非独立的 JS 逻辑,而是纯 CSS 实现:由 style/index.ts 定义的Keyframes驱动:

const skeletonClsLoading = new Keyframes(`ant-skeleton-loading`, { '0%': { transform: 'translateX(-37.5%)' }, '100%': { transform: 'translateX(37.5%)' }, });

动画时长、渐变色带等由设计 Token 控制(style/index.ts):

skeletonLoadingBackground: `linear-gradient(90deg, ${token.color} 25%, ${token.colorGradientEnd} 37%, ${token.color} 63%)`, skeletonLoadingMotionDuration: '1.4s',

即背景是一条 90 度方向的渐变(基础色 25% → 渐变末端色 37% → 基础色 63%),通过::after伪元素在 1.4 秒内往返平移形成扫光效果(style/index.ts)。官方演示见 demo/active.vue:<a-skeleton active />

三、组合子组件:SkeletonAvatar / SkeletonTitle / SkeletonParagraph

主组件内部将「头像 / 标题 / 段落」拆分为三个独立组件,位于 components/skeleton/ 目录下:

  • Avatar.tsx:渲染头像占位,接收sizeshape
  • Title.tsx:渲染标题占位,接收width
  • Paragraph.tsx:渲染段落占位,接收rowswidth

3.1 SkeletonAvatarProps

属性说明类型默认值
shape指定头像的形状circle|square-
size设置头像占位图的大小number |large|small|default-

从源码看,Avatar.tsx 内部对未传值的情况做了兜底:size默认'default'shape默认'circle'size传入数字时,会被转换为像素尺寸作用于宽高,逻辑见 Element.tsx:

const sizeStyle: CSSProperties = typeof size === 'number' ? { width: `${size}px`, height: `${size}px`, lineHeight: `${size}px`, } : {};

3.2 SkeletonTitleProps

属性说明类型默认值
width设置标题占位图的宽度number | string-

Title.tsx 的实现非常简洁:数字宽度会被拼接为px单位,字符串则原样作为 CSS 宽度:

const zWidth = typeof width === 'number' ? `${width}px` : width; return <h3 class={prefixCls} style={{ width: zWidth }} />;

因此width既可以是100(渲染为100px),也可以是'50%'这类百分比字符串。

3.3 SkeletonParagraphProps

属性说明类型默认值
rows设置段落占位图的行数number-
width设置段落占位图的宽度,若为数组时则为对应的每行宽度,反之则是最后一行的宽度number | string | Array<number | string>-

width的两种形态对应两种语义,见 Paragraph.tsx:

const getWidth = (index: number) => { const { width, rows = 2 } = props; if (Array.isArray(width)) { return width[index]; // 数组:逐行指定宽度 } // 非数组:仅作用于最后一行 if (rows - 1 === index) { return width; } return undefined; };
  • 传入数组(如['40%', '60%', '80%']):数组下标与行号一一对应,每一行使用对应宽度;
  • 传入单个值(number 或 string):只作用于最后一行,其余行撑满容器宽度。

注意rows默认值为 2(源码const { width, rows = 2 } = props),与前面主组件默认段落行数策略一致。

四、独立占位元素:SkeletonButton / SkeletonInput / SkeletonImage(3.0+)

从 3.0 版本起,Skeleton 还提供了三种可直接独立使用的占位元素,可通过Skeleton.ButtonSkeleton.InputSkeleton.Image或全局注册后的a-skeleton-button等标签使用(注册逻辑见 index.tsx)。

4.1 SkeletonButtonProps(3.0+)

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

Button.tsx 内部将size默认值设为'default'blocktrue时,按钮占位宽度扩展为父容器 100%,对应样式见 style/index.ts 中&-block规则。

4.2 SkeletonInputProps(3.0+)

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

Input.tsx 通过omit(skeletonElementProps(), ['shape'])剔除了输入框不适用的shape属性,同时支持block(源码类型定义SkeletonInputProps中包含block?: boolean,demo 中也演示了<a-skeleton-input style="width: 200px" />这种覆盖宽度写法)。

4.3 SkeletonImage(3.0+)

虽然官方 API 表未单独列出,但 Image.tsx 提供了图片占位:渲染一个内嵌的 SVG 图片图标(viewBox="0 0 1098 1024"),SkeletonImageProps剔除了sizeshapeactive三个属性。官方演示 demo/element.vue 中展示了按钮、头像、输入框、图像的完整组合及 Active / Block / Size / Shape 的联动控制。

4.4 尺寸与形状的底层统一处理

所有占位元素最终都收敛到 Element.tsx 这个函数式组件。它统一处理三类维度:

  • 尺寸large/small映射为-lg/-sm类名,数字映射为内联像素宽高;
  • 形状circle/square/round映射为对应形状类名;
  • 颜色与圆角:由 style/index.ts 中的genSkeletonElementButton等规则控制,尺寸分别对应设计 Token 的controlHeight/controlHeightLG/controlHeightSM(即 default / large / small 三档),按钮宽度为controlHeight * 2、输入框宽度为controlHeight * 5

五、样式定制:Token 与 ConfigProvider 联动

Skeleton 的样式通过 style/index.ts 中的genComponentStyleHook('Skeleton', ...)接入 ant-design-vue 的主题系统。它对外暴露两个ComponentToken(style/index.ts):

export type ComponentToken = { color: string; // 骨架块的基础填充色 colorGradientEnd: string; // 动画渐变末端色 };

其默认值由主题 Token 映射而来(style/index.ts):

token => { const { colorFillContent, colorFill } = token; return { color: colorFillContent, colorGradientEnd: colorFill, }; }

即基础色使用colorFillContent、渐变末端色使用colorFill,这两个 Token 会随主题明暗模式自动切换。因此你可以通过全局主题配置覆盖SkeletoncolorcolorGradientEnd来定制骨架颜色。此外,Skeleton 内部还通过useConfigInject('skeleton', props)(见 Skeleton.tsx)接入 ConfigProvider,继承prefixClsdirection,支持全局组件前缀修改与 RTL 布局(-rtl类名见 Skeleton.tsx)。同时round属性可将标题、段落圆角切换为胶囊形状(borderRadius: 100,见 style/index.ts)。

六、与 Spin 的选型对比

官方文档明确指出「可以被 Spin 完全代替,但是在可用的场景下可以比 Spin 提供更好的视觉效果和用户体验」。实际选型建议:

  • 内容区域为整块加载、无需预先呈现布局轮廓时,使用Spin即可;
  • 列表、卡片、详情页等有固定排版结构的区域,优先使用Skeleton:它按真实布局渲染占位,内容加载完成切换时页面不会大幅跳动;
  • 两者也可以组合使用:在骨架区域内的操作按钮上叠加Spin,兼顾「布局预告」与「局部加载反馈」。

七、快速上手总结

需求推荐写法
最简单占位(标题 + 段落)<a-skeleton />
带扫光动画<a-skeleton active />
头像 + 多行段落<a-skeleton avatar :paragraph="{ rows: 4 }" />(见 demo/complex.vue)
数据加载切换<a-skeleton :loading="loading">真实内容</a-skeleton>(见 demo/children.vue)
列表骨架<a-skeleton :loading="loading" active avatar>包裹a-list-item(见 demo/list.vue)
按钮 / 输入框占位<a-skeleton-button :active="active" /><a-skeleton-input :size="size" />(见 demo/element.vue)

掌握以上 API 与底层实现后,你可以在 ant-design-vue 项目中精准选用 Skeleton 的各类组合与独立元素,并通过 Token 定制与 ConfigProvider 联动让骨架屏与你的设计系统保持一致。如需进一步研究源码,可重点阅读 Skeleton.tsx(组合与默认布局策略)、Element.tsx(尺寸/形状统一处理)与 style/index.ts(Token 与动画实现)。

【免费下载链接】ant-design-vue🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜项目地址: https://gitcode.com/gh_mirrors/an/ant-design-vue

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询