搜索文章

输入关键词开始搜索

基于开源客户端二次开发,UI组件库应该怎么选

程序设计#架构

基于开源客户端二次开发,UI组件库应该怎么选

最近在规划一个基于开源 Agent Runtime 的桌面客户端,UI 开发很快遇到一个具体问题:到底要不要引入第三方组件库?是用 MUI、Ant Design 快速搭页面,还是所有组件都自己写?

一开始我把它理解成一个二选一问题。完整组件库开发快,但后面不好定制;全部手写最自由,但 Dialog、Select、焦点管理和无障碍都要重新做。把现有前端和目标客户端逐项看了一遍以后,结论变成了另一个问题:组件行为、视觉样式和设计 Token 最终由谁负责。

两个极端都有问题

全部手写看起来依赖最少,但很多基础组件并不简单。一个 Dialog 要处理焦点锁定、Esc、点击外部、层级、键盘导航和 Screen Reader。为了少一个依赖重复造这些轮子,投入不一定划算。

直接引入完整视觉组件库的问题则相反。它会带来自己的颜色、间距、阴影、状态、主题机制和组件 API。如果项目已有 Tailwind 和另一套 Token,再加一套 MUI 或 Ant Design,很快就会出现三个问题:

  • 同一种按钮有多套实现;
  • 修改品牌样式要覆盖大量内部 CSS;
  • 新页面不知道应该以哪套组件为准。

第一版可能很快,后面每次设计调整都在偿还混用成本。

我以前比较容易低估第三个问题。组件能不能改样式通常不是最大的障碍,真正麻烦的是团队开始形成两套使用习惯:有人遇到新需求先找完整组件库,有人继续写 Tailwind,还有人复制旧项目代码。过一段时间,同一个确认弹窗可能有不同焦点行为、不同按钮顺序和不同错误颜色。这个问题靠“后面统一一下”很难解决,因为统一意味着重新测试每个页面状态。

更合适的是分层拥有

当前方案选择的方向是:

React + TypeScript
  Tailwind CSS 4 + CSS Variables
    Radix primitives
      source-owned wrappers + CVA
        产品自己的业务组件

Radix 负责 Dialog、Tooltip、Select 这类基础行为和无障碍;项目自己拥有 wrapper 源码,用 CVA 管理 variant;Tailwind 只消费统一 Token;Lucide 负责图标。这样没有从零实现所有交互,也没有把视觉权威交给第二套完整组件系统。

这里的 source-owned 很重要。项目可以决定 Button、Dialog 和 Badge 的产品语义,可以统一 loading、error、permission、conflict 等状态,也可以在以后替换底层 primitive,而不需要业务页面直接依赖第三方复杂 API。

先确定 Token,再写页面

比较稳妥的层级是:

foundation
  → semantic
    → component
      → utility / variant

例如页面不应该到处写一个固定灰色,而应该使用 text-muted;错误提示不应该自行挑红色,而应该使用统一的 danger semantic token。设计稿中的 raw value 也不能直接复制到每个组件里,要先映射到现有 Token。

这样做看起来比直接照着截图写慢一点,但它能避免设计稿、旧项目和新客户端各自成为一套权威。后面做暗色主题、200% 缩放、Reduced Motion 和视觉回归时,也有明确入口。

不要为“以后可能用到”引入重型库

AI 视频客户端很容易联想到无限画布、专业时间线、波形编辑、任意 Dock 面板。于是 React Flow、Konva、Video.js、波形库和 IDE layout 都想先装上。

但第一版真正需要的通常只是明确的 Workspace、对话区、Artifact 面板、原生视频预览和有限的双栏/三栏布局。重型依赖应该由真实交互需求触发,而不是由产品想象触发。

每加一个依赖,至少要回答:

  • 具体解决哪个已确认场景?
  • 原生能力或现有 primitive 为什么不够?
  • bundle、安全、许可证和维护成本是什么?
  • 如果后面删除,业务页面会不会被它绑死?

答不出来就先不加。

同时也不要把“暂时不引入”写成永久禁令。比如以后真的出现逐帧标注、可编辑波形或复杂节点关系,就应该用真实 fixture 测试现有方案,再评估专业库。重点是让依赖跟着已经确认的交互进入,而不是先安装一组库,再反过来为它们寻找使用场景。

完整组件库什么时候更合适

对于一个短期内部管理工具,Ant Design 或 MUI 可能就是更好的选择。它们有完整表单、表格和后台页面能力,团队也可能已经非常熟悉。为了追求“纯净架构”而自己维护大量组件,反而会拖慢交付。

所以这个选择不能脱离产品。垂直创作客户端有较强品牌、媒体和 Artifact 交互,需要长期维护,自有 wrapper 更合适;一次性后台、设计要求不高且生命周期短,完整组件库完全可能更经济。

后续的开发顺序

后面我会按这个顺序推进 UI:

  1. 先冻结唯一 Token 和状态语义;
  2. 用 Radix 做少量无障碍 primitive;
  3. 包成项目自有的 Button、Dialog、Tabs 等组件;
  4. 再实现 Artifact、任务状态和媒体预览等领域组件;
  5. 新依赖必须带真实用例和退出条件;
  6. 通过 a11y、主题和视觉 diff 验收,不靠“看起来差不多”。