组件库日常巡检的关键检查项
组件库的问题很少只停留在组件库里。一个属性类型变化、全局样式泄漏或错误的导出方式,都会传到许多业务应用。日常巡检的价值,是在发布前发现这些影响,并告诉维护者具体变了什么,而不是等业务团队升级后再从页面异常倒查。
巡检项不宜一味增加。公共 API、交互行为、视觉结果、构建产物和发布信息是几条不同证据链,应分别给出结果。每条检查还要有负责人和处理方式:哪些变化必须阻断,哪些需要人工确认,哪些只作为趋势记录。
公共 API 先与上一版基线比较
组件的 props、导出名称和类型声明属于使用方可以依赖的接口。删除导出、把可选属性改为必填、缩小联合类型,通常都需要明确的迁移安排。仅靠搜索Props接口不够,因为类型可能通过别名、交叉类型、泛型或再导出暴露,多个组件也可能有同名属性。
更稳妥的方式是从真实入口生成声明文件或 API 报告,再与上一版已发布基线比较。差异由维护者确认:新增兼容能力可以接受,弃用项要保留说明,破坏性变化则匹配相应版本和迁移文档。工具负责找差异,是否破坏兼容仍需要结合类型语义判断。
巡检还应放一个最小消费者项目。它只从包的公开入口导入常用组件,完成类型检查和构建。这样能发现声明文件虽然生成成功,实际exports、路径或模块格式却无法被使用的问题。消费者项目不要引用组件库源码,否则会绕过真正的发布边界。
行为测试比快照更接近用户
按钮能不能获得焦点,弹窗能否用键盘关闭,表单错误是否与输入关联,这些行为无法由像素截图充分证明。基础组件应优先保留面向角色和可访问名称的交互测试,再使用视觉回归捕捉布局、颜色和间距变化。
弹窗、下拉框和浮层要覆盖打开、关闭、焦点返回、滚动和叠层。异步组件则测试加载、空数据、错误和取消。测试不必枚举所有业务组合,但需要覆盖组件承诺支持的状态。发现问题时,报告应指出具体 Story、操作和断言,不能只给一张失败截图。
视觉回归需要稳定环境
截图差异会受浏览器版本、字体、动画、时区和数据变化影响。基线应在固定浏览器和字体环境中生成,Story 使用静态数据,隐藏当前时间与随机内容。阈值根据组件特点确定,不要复制一个极小比例后把所有差异都当噪声,也不要设置得过宽而漏掉真正变化。
下面的 Playwright 示例固定视口、等待字体并关闭动画。componentsToTest应来自维护过的 Story 清单,新增公共组件时同步补充,而不是假定自动遍历出的每个 Story 都适合截图。
import { test, expect } from '@playwright/test'; const componentsToTest = [ { name: 'Button Primary', storyId: 'components-button--primary' }, { name: 'Modal Danger', storyId: 'components-modal--danger' }, { name: 'Table Empty', storyId: 'components-table--empty' }, ]; test.describe('component visual regression', () => { for (const { name, storyId } of componentsToTest) { test(name, async ({ page }) => { await page.setViewportSize({ width: 1024, height: 768 }); await page.goto(`/iframe.html?id=${storyId}&viewMode=story`); await page.locator('#storybook-root').waitFor(); await page.evaluate(() => document.fonts.ready); await expect(page.locator('#storybook-root')).toHaveScreenshot( `${storyId}.png`, { animations: 'disabled' }, ); }); } });差异出现后先看原因,再决定更新基线。预期中的设计调整,需要附上变更说明和评审记录;字体没加载、动画未停或测试数据变化,则修复测试环境。直接批量接受新截图,会让视觉回归失去意义。
构建产物要用实际导入方式验证
包体积巡检不能只看组件库输出目录总大小。业务应用可能只导入一个 Button,却因为入口副作用、聚合导出或打包配置把整库带入。准备一个小型消费者构建,分别测试常见按需导入方式,再查看产物中实际包含的模块,会比单纯压缩文件大小更可靠。
体积预算应基于当前基线和使用场景。超出预算先列出新增依赖、重复版本和不可摇除模块,不必立即把一次合理增长判为错误。若变化来自新能力,维护者可以评估拆分入口;若是不小心全量导入图标或语言包,报告应给出依赖路径。
sideEffects声明也要与真实代码一致。包含全局 CSS、注册逻辑或 polyfill 的文件不能为了 Tree-Shaking 随意标为无副作用。更好的做法是把全局入口与纯组件入口分开,让使用方显式选择。
样式边界需要专门巡检
组件库应避免无意修改body、通用标签和业务类名。基础 Reset 如果是产品约定,就作为单独入口提供并写明作用范围,不要随某个组件导入。CSS Modules、CSS-in-JS 或命名前缀能减少冲突,但仍要检查 Portal、全局变量和第三方样式的实际输出。
可以在一个带有故意冲突样式的宿主页中渲染组件,观察双方是否互相覆盖。主题变量则测试默认值、局部覆盖和缺失值。暗色主题或高对比模式若是公开能力,也应放入行为与视觉用例,而不是只在文档示例里出现。
巡检结果要进入发布决策
每次发布前汇总 API 差异、行为测试、视觉变更、消费者构建和体积变化。报告链接到具体产物,并标明阻断项、待确认项和已知限制。弃用 API 要给迁移办法和预计移除版本,不能只在类型上加一行注释。
不是所有检查都需要放在本地 Git 钩子里。快速的类型与单元测试适合开发阶段运行,完整浏览器矩阵和消费者构建可以交给 CI。把昂贵任务塞进每次推送,往往只会促使开发者绕过检查。
日常巡检最终要回答一个简单问题:这次发布会让现有使用方看到哪些变化,他们如何验证和回退。能持续回答这个问题,组件库才能在频繁迭代时保持可预测,而不是靠维护者记住所有潜在影响。