Gutenberg Spinner 组件深度解析:`@wordpress/components` 加载指示器的实现原理与迁移指南
2026/9/17 19:52:53 网站建设 项目流程

Gutenberg Spinner 组件深度解析:@wordpress/components加载指示器的实现原理与迁移指南

【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg

Spinner是 Gutenberg 中用于提示"用户操作正在处理中"的轻量级反馈组件。本文以组件文档 packages/components/src/spinner/README.md 为主体,结合 组件源码 与 样式文件 深入讲解其渲染结构、动画实现与主题集成方式,并说明当前仓库中该组件的最新推荐用法与迁移路径。

一、组件定位与适用场景

按 组件文档 的定义,Spinner用于通知用户其触发的操作正在被处理。文档同时给出了明确的最佳实践边界:

Spinner 应当:向用户传达"请求正在处理中、且即将完成"的信号。

也就是说,Spinner 适合时长不可预估的异步操作(内容保存、媒体上传、服务端渲染等待等),不适合表示可精确计时的进度——那种场景应使用进度条类组件。

需要注意:该组件渲染的是纯装饰性 SVG,自身不携带任何文本或 ARIA 标签(role="presentation"focusable="false"),因此无障碍信息必须由外层容器补充(例如配合 visually-hidden 文本或带aria-busy的父元素)。这一点从 源码中的 SVG 属性 可以直接确认。

二、基本用法

文档给出的标准用法:

import { Spinner } from '@wordpress/components'; function Example() { return <Spinner />; }

组件通过forwardRef导出(见 index.tsx 第 56-58 行),因此支持将 ref 直接转发到内部 SVG 节点:

import { Spinner } from '@wordpress/components'; import { useRef } from '@wordpress/element'; function Example() { const ref = useRef<SVGSVGElement>(null); // ref.current 指向 <svg class="components-spinner" ...> return <Spinner ref={ref} />; }

forwardRef的意义在于:当 Spinner 嵌入按钮、通知条等复合组件时,外层逻辑仍能拿到真实的 SVG DOM 节点,用于尺寸测量、动画控制或无障碍关联(如aria-describedby指向的隐藏提示文本)。

三、渲染结构:双图层 SVG 设计

UnforwardedSpinner 渲染一个viewBox="0 0 100 100"的 SVG,内含两个图层:

图层元素路径/参数作用
轨道(track)<circle>cx="50" cy="50" r="50",即完整圆周灰色底环,标示转动的完整轨迹
指示弧(indicator)<path>d="m 50 0 a 50 50 0 0 1 50 50"从顶点顺时针转 90° 的弧段,旋转形成"彗尾"效果

两个关键细节:

  1. vectorEffect="non-scaling-stroke":两个图形都声明了该属性,其含义是描边宽度不参与 viewBox 缩放。因此 Storybook 故事 中注明"Spinner 可以缩放到任意尺寸,但描边宽度保持不变"——CustomSize故事正是通过style: { width: space(20), height: space(20) }将尺寸从 16px 放大到 20px 来验证这一行为。这解释了为什么组件在默认 16px 尺寸下使用 1.5px 描边不会随缩放失真。
  2. {...props}透传:组件签名{ className, ...props }: WordPressComponentProps<{}, 'svg', false>表明除className外的所有 SVG 原生属性都会透传给根元素,例如width/height可在调用侧直接覆盖默认的 16px。
// 通过透传属性自定义尺寸(描边宽度仍保持 1.5px) <Spinner width={24} height={24} />

四、样式与动画实现

style.module.scss 采用 CSS Modules(styles.spinner等类名在编译时哈希隔离),核心样式如下:

.spinner { width: 16px; height: 16px; display: inline-block; margin: 5px 11px 0; position: relative; color: $components-color-accent; // 主题强调色 overflow: visible; opacity: 1; background-color: transparent; } .track, .indicator { fill: transparent; stroke-width: 1.5px; } .track { stroke: $components-color-gray-300; // 轨道:中性灰 } .indicator { stroke: currentColor; // 指示弧:取 .spinner 的 color stroke-linecap: round; // 圆头端点,弧段两端更柔和 transform-origin: 50% 50%; animation: spin 1.4s linear infinite both; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

实现上有三个值得注意的设计决策:

  • 主题集成的两层机制.spinnercolor取自$components-color-accent(主题强调色),.indicatorstroke则写为currentColor。这样指示弧颜色始终跟随组件色值,而overflow: visible保证旋转中的弧段在 16px 容器内不会被裁切。
  • 动画参数1.4s linear infinite both——匀速旋转、无限循环,both填充模式确保动画在应用前/后都保持初始旋转状态,避免首帧闪烁。
  • 动画不随prefers-reduced-motion关闭:新版 UI 包样式 中有一条注释明确说明,旋转动画"即使用户设置了减少动态偏好也会保留",并引用 WCAG 2.2 关于pause-stop-hide准则的 Note 4——因为 Spinner 本身时长不可控,若静止显示会被误认为已完成,保留动画反而更符合该场景的可访问性要求。

五、Storybook 中的行为验证

Storybook 故事文件 提供了两个可交互案例:

  • Default:直接渲染<Spinner {...args} />,验证默认 16px 外观;
  • CustomSizeargs = { style: { width: space(20), height: space(20) } },验证"任意尺寸缩放 + 描边宽度不变"的承诺。

故事的元数据还揭示了组件的当前状态:

componentStatus: { status: 'not-recommended', whereUsed: 'global', notes: 'Use `Spinner` from `@wordpress/ui` instead.', },

六、从@wordpress/components迁移到@wordpress/ui

当前仓库中该组件已被标记为not-recommended,官方建议迁移到@wordpress/ui包中的新版 Spinner。两个实现的对比:

维度@wordpress/components@wordpress/ui
源码位置packages/components/src/spinner/index.tsxpackages/ui/src/spinner/spinner.tsx
类型签名WordPressComponentProps<{}, 'svg', false>标准ComponentProps<'svg'>,ref 明确为SVGSVGElement
类名前缀components-spinner+ 模块哈希类wp-ui层内的模块哈希类
尺寸固定 16px(SCSS)设计令牌var(--wpds-dimension-size-2xs)
轨道色$components-color-gray-300var(--wpds-color-background-track-neutral)
指示弧色currentColor(强调色)var(--wpds-color-background-thumb-brand)
CSS 组织顶层 SCSS 模块@layer wp-ui内嵌套components层,见 style.module.css

新版 Spinner 实现 保留了完全相同的几何结构(同样的 viewBox、circle + path、non-scaling-stroke、1.4s 旋转动画),主要变化是全面切换到 WPDS 设计令牌--wpds-*CSS 变量)并使用 CSS@layer组织级联优先级,使其能参与新的设计系统主题体系。迁移方式即调整 import 来源:

// 旧 import { Spinner } from '@wordpress/components'; // 新 import { Spinner } from '@wordpress/ui';

新版组件的行为同样有测试覆盖,见 spinner.jsdom.test.tsx。

七、小结

  • Spinner 的本质是"灰色轨道 + 主题色弧段"的双图层 SVG,通过transform: rotate的 1.4s 匀速循环动画形成旋转指示;
  • vectorEffect="non-scaling-stroke"让组件可安全地按任意尺寸缩放而不改变 1.5px 描边,这一行为由 Storybook 的CustomSize故事专门验证;
  • 组件本身是装饰性元素(role="presentation"),调用方需自行补充语义信息;
  • 在新代码中应优先使用@wordpress/ui的 Spinner;@wordpress/components版本仍保留在全局使用范围(whereUsed: 'global'),但新接入请遵循not-recommended的标记完成迁移。

【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg

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

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

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

立即咨询