关联公司 Testlify
进行中

Testlify - 共享组件库

主导了在 Testlify 4 个应用间引入共享 Vue 组件库的项目,以私有包的形式发布,消除了各应用间重复的 UI 代码。

Testlify 由 4 个独立的 Web 应用组成一整套产品。此前,每个产品团队都各自独立开发和设计通用 UI 组件,导致工程投入重复、各产品间视觉不一致。我提出了这个想法,并带领团队构建了一个共享的 Vue 3 组件库,以此规范 UI 开发。

我做了什么

  • 架构整合: 主导了把通用 UI 原语整合进一个中心化库的项目,在各团队间建立起统一的设计 token 和组件契约。
  • 无状态组件设计: 让库中的组件严格保持无状态且只做展示,使它们能干净地集成进 4 个应用中的任意一个,同时与业务逻辑解耦。
  • 私有包分发: 把这个库作为带版本管理的包发布并维护在 GitHub Packages 上,让各团队可以通过标准的依赖更新来采用 bug 修复和新组件,而无需重复代码。

为什么它有意义

把 UI 开发集中到一个共享包里,消除了 4 个应用间的冗余代码,加快了功能交付周期,也确保了 Testlify 整个产品套件拥有统一一致的设计系统。

主导,而非单人完成

我提出了这个库的想法,并主导了架构设计,但这不是一个人完成的:自 2026 年 5 月以来,7 位贡献者共提交了 86 次,我自己的提交大约占其中三分之二。要让一个共享设计系统被 4 个产品团队采用,光写组件是不够的,还需要一套能让采用变得低风险的发布流程,而这才是真正的难点所在。

在不为每个 PR 都发布的情况下测试尚未发布的组件改动

一个共享库最大的采用障碍不是写组件本身,而是“我改了点东西”和“使用方团队真的能试用它”之间的落差。给每一个 PR 都发布一个临时版本,会给一个大多数 PR 根本不会被任何地方安装的包带来真实且反复发生的 CI 成本,所以这是按需触发的:只有当确实有使用方需要测试时,PR 才会被打上 needs-snapshot 标签,从而触发一个 snapshot.yml GitHub Action,发布一个带临时 @pr<number> 标签的包。配套的 cleanup-snapshot.yml 会在 PR 关闭后移除这个标签。常规发布通过 Changesets 走两条通道:来自 main 的 @latest,以及来自预发布分支的 @beta,发布流程在 CI 通过后还会自动合并它自己生成的 “Version Packages” PR。

一个强制自身依赖方向的单体仓库

这个库被拆分成了多个层级,而不是一个扁平的包:tokens、icons 和 styles 位于最底层,没有任何内部依赖;core 建立在它们之上;ds 建立在 core 之上;ds-patterns 则建立在 ds、core 和 styles 之上。每一层只能向下依赖,绝不能向上依赖,因此一个底层原语不会在不知不觉间对一个更高层的模式组件产生依赖。ds-patterns 也不只是通用原语:它还包含真实的 Testlify 业务领域组件(比如一个 CandidateCard),构建在其他所有组件所用的同一套原语层之上。Storybook 启用了无障碍插件,为每个组件编写文档并公开部署,因此使用方团队(或外部审阅者)无需拉取代码仓库就能检查组件的各种状态和无障碍检测结果。每个组件还都带有专门的 .a11y.spec.ts 文件,在 CI 中运行真实的 axe-core 检查,而不只是一个供人工查看的 Storybook 插件。

数据模型与规模

  • 6 个已发布的包(ds、-core、-icons、-patterns、-styles、-tokens),各自独立进行版本管理。
  • 243 个测试文件(Vitest + Vue Test Utils)直接对这个库本身进行测试,而不只是测试使用它的应用。
  • 9 个 GitHub Actions 工作流,覆盖 CI、预览与生产部署、Storybook 部署、预发布与正式发布、PR 快照发布及快照清理。
  • pnpm workspaces 加上 Turborepo 的缓存,把构建任务的范围限定在某次改动实际影响到的包上;测试则直接通过 Vitest 在整个 workspace 里运行,而不经过 Turbo 的任务图。

采用是按团队逐步推进的,而不是强制统一的切换日:招聘方/管理后台应用 目前已经用上了全部 6 个已发布的包,而其他产品团队则是按各自的发布节奏引入单个组件,而不是 4 个应用一次性统一切换。

自研 vs 采购

没有手写版本升级脚本:Changesets 从每个 PR 一个 changeset 文件出发,管理全部 6 个包的版本和变更日志。没有自建私有仓库:GitHub Packages 复用了已经用于控制仓库访问的同一套 GitHub 身份验证,而不是另外运行和维护一个像 Verdaccio 这样的独立仓库。

技术栈

Vue 3、TypeScript、Vite、pnpm workspaces 配合 Turborepo、Storybook 及无障碍插件、Changesets 用于版本管理、Vitest 和 Vue Test Utils,通过 GitHub Packages 发布。