Ant Design Skeleton 包含子组件(children)模式:用 loading 在骨架屏与真实内容间平滑切换
2026/9/9 19:40:42 网站建设 项目流程

Ant Design Skeleton 包含子组件(children)模式:用 loading 在骨架屏与真实内容间平滑切换

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design

本篇以 Ant Design 组件库中 Skeleton 的 "Contains sub component"(包含子组件)官方示例为切入点,详解如何把真实页面内容直接作为<Skeleton>的 children 传入,并通过一个loading状态在加载占位与真实内容之间自动切换。读完本文,你将掌握 Skeleton 的两种渲染语义(纯占位符 / 内容包装器)、示例中setTimeout模拟异步加载的写法,以及这套模式在真实数据请求场景下的落地方案。

示例的定位:Skeleton 不止是占位符

在 Ant Design 组件目录中,Skeleton 一共有 8 个官方示例,分别覆盖基础用法、复杂组合、动画、独立元素(Button/Avatar/Input/Image/Node)、列表、语义化样式与组件 Token(见 components/skeleton/index.en-US.md 中的 Examples 清单)。其中 "Contains sub component"(components/skeleton/demo/children.md)对应的中英文描述非常简短:

  • zh-CN:加载占位图包含子组件。
  • en-US:Skeleton contains sub component.

它演示的核心能力是:Skeleton 可以像布局容器一样包裹真实的业务内容,并在加载过程中用骨架屏"顶替"这些内容。只有当数据就绪、loading变为false时,真实内容才会出现在原位,从而避免内容从骨架到正式页面的突兀跳动。

从 demo 源码读懂推荐写法

示例的完整实现位于 components/skeleton/demo/children.tsx,核心代码非常短,可复制到本地直接运行:

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> ); }; export default App;

可以拆解为三个组成部分来理解。

1. 用 useState 管理加载状态

const [loading, setLoading] = useState<boolean>(false)是整套机制的控制开关。初始为false,即页面刚渲染时直接展示真实内容;点击按钮后才进入"加载中"状态。在实际业务中,这个布尔值通常来自异步请求的生命周期,而非手动开关。

2. 用 setTimeout 模拟数据到达

showSkeleton先把loading置为true,随后通过setTimeout(..., 3000)在 3 秒后把loading置回false,模拟"发起请求 → 等待网络 → 数据返回"的完整过程。这里使用 3 秒是为了让骨架动画效果肉眼可见;在真实工程里,这一步应替换为请求完成回调或Promise.then/async/await中的收尾逻辑。

3. 内容放在 Skeleton 内部作为 children

真实内容(标题<h4>与段落<p>)直接作为<Skeleton>的子节点,这是该示例区别于其他占位示例的关键点——其余示例(如 Basic、Complex)通常只展示纯骨架块,而这里 Skeleton 与内容共享同一个 DOM 位置。

按钮同时承担"触发加载"和"禁用保护"两个职责:loading期间按钮被disabled,防止重复触发,这在真实异步场景中同样是常见的防重入手段。

源码级原理:loading 属性背后的两条渲染分支

要真正掌握这种写法,需要回到组件的核心实现 components/skeleton/Skeleton.tsx。在组件内部,渲染逻辑被一段条件语句切分成两种完全不同的输出:

if (loading || !('loading' in props)) { // ...拼接 avatar / title / paragraph 骨架块并渲染占位 DOM return ( <div ref={nativeElementRef} className={cls} style={mergedStyles.root}> {avatarNode} {contentNode} </div> ); } return children ?? null;

这段代码揭示了 Skeleton 对loading的精确语义:

  • 未显式传入loading属性'loading' in props为假)时,无条件渲染骨架屏。这是 Skeleton 的默认形态,适合"内容尚不存在、需要先给出一块占位"的场景。
  • 显式传入loading={true}时,同样渲染骨架屏。
  • 显式传入loading={false}时,直接返回children ?? null——即渲染包装的真实内容;若没有 children,则渲染null(页面不留空壳)。

从 components/skeleton/index.en-US.md 的 API 表格可以确认,loading的类型为boolean,默认值为-(即不传时不参与判断、走"永远渲染骨架"的分支),正是为了实现上面的语义设计。

分支内部:骨架屏的自动排版

值得留意的是,demo 中只写了<Skeleton loading={loading}>,没有指定任何内部块。此时组件会套用默认值avatar = falsetitle = trueparagraph = true(见 Skeleton 组件内部解构赋值)。渲染时通过getAvatarBasicProps/getTitleBasicProps/getParagraphBasicProps三个辅助函数,依据"是否有头像、是否有标题、是否有段落"的组合自动推算骨架块的默认几何参数。因此示例中呈现的是一段标题加两行段落的经典骨架布局,且标题占宽、段落行数都由算法自动匹配,无需手工配置。

测试用例对 children 渲染行为的验证

组件仓库中的单元测试(components/skeleton/tests/index.test.tsx)从多个角度验证了 children 与loading的配合行为,可作为理解语义的第二重证据:

// 显式 loading={false} 且无 children 时渲染为空 it('should display without children and falsy loading props', () => { const { asFragment } = render(<Skeleton loading={false} />); expect(asFragment().firstChild).toMatchSnapshot(); }); // 显式 loading={false} 且 children 为 0 时,文本内容保留 it('should display with empty children and falsy loading props', () => { const { container } = render(<Skeleton loading={false}>{0}</Skeleton>); expect(container.textContent).toBe('0'); }); // 显式 loading={false} 且 children 为数组时按序输出文本 it('should display children', () => { const { container } = render(<Skeleton loading={false}>{[1, 2, 3]}</Skeleton>); expect(container.textContent).toBe('123'); });

这三条用例分别覆盖了"无内容""内容为假值0""内容为数组"的边界情况:只要loadingfalse,Skeleton 就退化为纯粹的内容透传容器,甚至{0}这样的"空内容"也会被原样渲染,而不会被当成空节点吞掉。这从测试层印证了return children ?? null的实现:只有 children 为null/undefined时才真正渲染空。

何时应该使用"包含子组件"这种写法

回到官方文档对 Skeleton 使用场景的界定(components/skeleton/index.en-US.md):

  • 某个资源需要较长时间加载时;
  • 组件包含大量信息(如 List、Card 这类信息密集型界面)时;
  • 仅在首次加载数据时生效;
  • 任何可用 Spin 的场景都可改用 Skeleton,且通常能带来更好的体验。

"包含子组件"模式恰好服务于其中"首次加载"这一场景:真实内容结构复杂、占据较大的页面空间,如果直接整块显示 Spin 图标,页面布局会显得空洞;而用同一结构的骨架屏占位,再在数据返回后原位切换到内容,视觉连续性强,用户能预先感知页面将要呈现的信息层级。

迁移到真实异步请求的推荐写法

官方示例用setTimeout做演示,迁移到真实项目时,把开关与请求绑定即可:

import { useEffect, useState } from 'react'; import { Skeleton } from 'antd'; interface Article { title: string; content: string; } const ArticleDetail = () => { const [loading, setLoading] = useState(true); const [article, setArticle] = useState<Article | null>(null); useEffect(() => { fetchArticle() .then((data) => { setArticle(data); setLoading(false); }) .finally(() => { // 即使失败也要结束 loading,配合 ErrorBoundary/异常 UI 展示错误态 }); }, []); return ( <Skeleton loading={loading}> <h4>{article?.title}</h4> <p>{article?.content}</p> </Skeleton> ); };

几条实用建议,与官方示例精神一致:

  • 状态机化:把loadingerrordata分开管理,loading只表达"请求进行中";请求失败时同样要置loadingfalse,避免骨架屏永久停留。
  • 切换不可逆触发:如示例中按钮disabled={loading}所示,在加载期间阻止用户再次触发同一请求。
  • 骨架与内容结构对齐:children 是标题 + 段落时,保持 Skeleton 默认的title/paragraph组合即可;若真实内容是头像 + 文本结构,可开启avatar让骨架更接近最终布局,进一步降低加载完成瞬间的落差感。
  • 内容就位后再关闭 loading:把setLoading(false)放在数据真正写入 state 之后(如上例在setArticle之后执行),确保切换瞬间 children 里已有真实数据可渲染。

小结

Skeleton 的 "Contains sub component" 示例用 30 行不到的代码,示范了 Ant Design 中最优雅的加载过渡方案之一:用同一个loading布尔值,在两个渲染分支(骨架占位与真实 children)之间来回切换。其底层依赖 Skeleton.tsx 中loading || !('loading' in props)的判断——未传loading时 Skeleton 是纯粹的静态占位符,传入后则成为带状态的容器。配合组件的titleparagraphavatar等占位配置与单元测试验证的边界行为,这套模式足以覆盖从简单文本到信息密集型 List/Card 的大多数"首次加载"体验优化需求。

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design

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

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

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

立即咨询