从静态孤岛到动态系统:AI 时代的前端组件库工程进化论
在前端工程与设计系统的交叉领域,一场深刻的范式革命正在悄然发生。长期以来,我们习惯于一个稳定且线性的工作流:设计师在 Figma 等工具中创建精美的组件(Component),交付给工程师,再由工程师将其翻译为代码。这个以视觉结构为中心,在“设计 → 组件 → 规格 → 开发”路径上运转的模式,构成了现代设计系统的基石。然而,随着生成式 AI(GenAI)技术的崛起,尤其是动态图形用户界面(Dynamic GUI)需求的激增,这套行之有效的体系正面临前所未有的挑战。
传统的 Figma 组件,本质上是静态的视觉蓝图。它能完美地定义一个按钮的颜色、圆角和字号,却难以承载其背后的复杂交互、多维状态和动态数据逻辑。当界面从固定的“页面(Page)”集合,演变为可自我组织、实时响应的“行为系统(Behavior System)”时,设计与工程之间的鸿沟被迅速拉大。我们发现,真正的用户体验,越来越多地发生在代码的动态逻辑之中,而非 Figma 的静态画板之上。
这引发了一个核心问题:如果设计的最终产物是一个可运行、可交互的软件实体,那么我们的“单一事实来源(Single Source of Truth)”究竟应该是设计稿,还是代码?本文将深入探讨这一变革,提出“可执行 UI 组件(Executable UI Component)”作为新一代设计系统资产的核心,并剖析其背后“代码即规格(Code = Spec)”的理念转变、全新的“设计 → 代码 → Token 反向同步”工作流,以及这一进化对前端工程、设计角色乃至整个产品开发协作模式的深远影响。
范式迁移:从“设计为中心”到“代码即规格”
要理解这场变革的必然性,我们首先需要回溯传统设计系统的工作模式,并审视其在 AI 时代暴露出的核心矛盾。
过去:以视觉结构为中心的线性协作
在传统的模型中,整个流程是单向且割裂的:
- 设计(Design) :UX/UI 设计师在 Figma 中绘制界面,定义组件的视觉样式,并创建组件库。这是创意的起点。
- 规格(Spec) :通过设计标注工具或设计规范文档,设计师将组件的视觉属性(颜色、间距、字体等)传递给开发团队。这份“规格”是沟通的桥梁。
- 开发(Development) :前端工程师依据规格,用代码“复刻”设计稿,并实现静态视觉稿中缺失的交互逻辑、状态管理和数据绑定。
- 割裂的维护:当需求变更时,流程通常需要从“设计”端重新发起。设计稿的更新与代码库的维护是两个独立进行的任务,极易导致两者之间的“规范漂移(Spec Drift)”。
这个模式的根基在于一个核心假设:设计是源头,代码是实现。Figma 组件库被视为权威的设计资产,而代码库则是其工程化的产物。然而,这种模式的致命弱点在于,它将一个完整的“UI 组件”人为地割裂成了两个部分:
- 静态的“皮囊” :存在于 Figma 中,定义了“它看起来怎么样”。
- 动态的“灵魂” :存在于代码中,定义了“它如何工作”。
在界面相对静态的时代,这种分离尚可容忍。但在 GenAI 驱动的动态界面中,一个组件可能需要根据用户输入、上下文情境、乃至模型生成的内容,实时地改变其结构、内容和行为。此时,静态的设计稿已然不足以充当“规格”,因为它从一开始就缺失了最重要的部分——行为与逻辑。
现在与未来:Code = Spec,代码成为新的单一事实来源
AI 时代的需求迫使我们重新思考“组件”的定义。一个现代 UI 组件,远不止视觉呈现,它是一个封装了自身所有逻辑的、可独立运行的软件单元。这就引出了一个新的范式:代码即规格(Code = Spec) 。
在这个新范式中,组件的 TypeScript/JavaScript 代码(及其附带的类型定义、测试用例和文档)本身就成为了最完整、最精确、最权威的“设计说明”。它不再是对设计稿的被动翻译,而是集视觉、行为、状态和数据于一体的“最终实体”。
这种转变意味着工作流的核心发生了根本性的迁移。我们不再试图去“同步”设计稿和代码,而是将代码库( Repository ) 视为设计系统的真正载体。Figma 等设计工具的角色也随之演变,从“最终蓝图”转变为一个服务于代码创造过程的“视觉化 IDE(Visual IDE)”或“灵感画板”。设计师依然可以在其中探索视觉创意,但其产出不再是需要工程师像素级复刻的“圣旨”,而是可以直接生成高质量组件代码的“起点”。
可执行 UI 组件:现代设计资产的四个维度
当“代码即规格”成为共识,我们所谈论的“组件”也随之进化为“可执行 UI 组件”(Executable UI Component)。它不再是静态的视觉模块,而是一个自包含、可运行的实体。一个完整的可执行 UI 组件,应从以下四个维度进行定义,这构成了它与传统组件的本质差异。
1. 视觉结构 (Visual Structure)
这是组件最基础的层面,定义了“它看起来是什么样子”。它包括 DOM 结构、样式(CSS)、布局以及由 Design Token 驱动的视觉属性(如颜色、字体、间距)。在 AI 时代,视觉结构本身也可能是动态的,例如根据内容或状态自适应调整布局。
2. 状态与行为 (State & Behavior)
这是组件的“灵魂”,定义了“它如何工作”。
- 状态 (State) :通过内置的状态机(State Machine)或声明式状态管理,精确描述组件的所有可能状态(如
loading,error,disabled,empty)及其流转条件。 - 行为 (Behavior) :定义组件如何响应用户交互和系统事件。这包括事件处理逻辑、动效(Animation)以及与其他组件的通信机制。
3. 数据结构 (Data Schema)
这定义了组件“消费”什么样的数据,即其内容和配置的“形状”。通过 TypeScript 类型定义或 JSON Schema 等形式,组件与其使用者之间建立起一份严格的“数据合约”。这份合约确保了传入的数据符合预期,也使得组件在不同应用和上下文中具有可预测性。
4. 交互逻辑 (Interaction Logic)
这是对行为的进一步封装,定义了更高级别的用户任务流程。例如,一个“文件上传”组件不仅包含点击上传的“行为”,还封装了文件选择、进度显示、上传成功/失败处理、重试等一系列交互逻辑。
将这四个维度封装于一体,可执行 UI 组件从一个被动的“视图”转变为一个主动的“微型应用”。这种转变对工程实践带来了深远的影响:
- 契约式 Props:组件的
props不再是零散的配置项,而是遵循数据合约的结构化对象。这使得组件的 API 更加稳健和易于理解。 - 运行时组合(Runtime Composition) :组件可以像乐高积木一样在运行时被动态地组合和替换,以构建复杂的、自适应的界面。
- 可观测性 ( Observability ) :由于状态和行为被明确地建模,我们可以轻松地为组件添加日志、埋点和遥测,从而精确地观测其在真实环境中的运行状况。
- 内聚 的测试:测试不再局限于视觉快照(Snapshot Testing),而是可以覆盖状态流转、交互逻辑和数据处理的全过程,大大提升了组件的质量和可靠性。
新工作流:从“设计到代码”到“代码驱动设计”
“可执行 UI 组件”的确立,催生了一种全新的、循环驱动的工作流:Design → Code → Token reverse sync。这标志着设计与开发的关系从单向传递演变为双向协作。
这个工作流的核心思想是:以代码为中心进行创造,然后从代码中提炼出设计资产,反向同步给设计系统。
具体流程如下:
-
设计 → 代码 (Design → Code) 这个阶段的目标是快速地将设计灵感转化为高质量、可执行的组件代码。这可以有多种实现方式:
- AI ****辅助生成:利用
v0.dev、Copilot等工具,设计师或工程师可以直接通过自然语言描述、草图甚至设计稿截图,快速生成组件的初始代码框架。 - 传统手动编码:对于逻辑复杂或需要精细调优的组件,工程师依然可以基于设计稿进行手动编码。但这里的重点是,编码过程即是“设计”过程,工程师需要与设计师紧密合作,共同完善组件的行为和交互。
- AI ****辅助生成:利用
-
代码即规格 (Code = Spec) 一旦组件代码完成,它就成为该组件的唯一事实来源。这份代码(包括其
props类型定义、状态机模型、JSDoc 注释等)就是最详尽、最准确的规格说明书。 -
Token 反向同步 (Token Reverse Sync) 这是新工作流中最具革命性的一步。在组件代码稳定后,我们不再需要手动去维护一套独立的设计规范文档。取而代之的是,我们通过自动化工具,从代码中反向抽取 Design Token。这些 Token 包括:
- 视觉 Token:颜色(
color)、间距(spacing)、字体排版(typography)、圆角(borderRadius)等。 - 行为 Token:动效曲线(
easing)、持续时间(duration)。 - 交互标识:特定交互模式的命名(如
subtle-hover、destructive-click)。 - 这些被抽取的 Token 会被结构化地存储(例如存为 JSON 或 YAML 文件),并注入回 Figma 等设计工具中。由此,Figma 中的设计资产(如颜色库、文本样式)实现了由代码驱动的自动更新。
- 视觉 Token:颜色(
新工作流的收益与挑战
这种“代码驱动设计”的模式带来了显著的收益:
- 消除规范漂移:由于 Design Token 直接来源于生产代码,设计工具中的视觉规范与线上实际效果永远保持一致。
- 提升协作效率:设计师可以放心地使用由代码生成的最新 Token 进行创作,无需担心版本错配。工程师的重构或视觉调整也能即时、准确地反映到设计系统中。
- 强化系统治理:所有设计决策最终都沉淀为代码和版本化的 Token,使得设计系统的演进历史清晰可追溯,便于管理和审计。
然而,这一新工作流也带来了新的挑战,例如如何确保 AI 生成代码的质量与一致性?如何设计一套健壮的 Token 抽取与治理方案?以及,如何引导团队成员适应这种全新的协作模式?这些问题将在下一章节中深入探讨。
A2UI 概念与作用:让 Agent 直接“说 UI”
在前面我们更多是在讨论「组件库自己的演进」。但一旦把 Agent 引进来,问题会变成:Agent 要怎么、安全地、跨端地把“我要一个怎样的界面”这件事说清楚? A2UI 给出的答案可以概括成一句话:Agent 只说“界面长什么样”,不自己动手“写代码画界面”。
A2UI 是什么?
从工程角度看,A2UI 是一套 Agent → 客户端的声明式 UI 协议:
- Agent 输出的是一段 JSON,描述有哪些组件、怎么排布、和哪些数据绑定;
- 客户端用自己的组件库和样式体系去渲染这些 JSON;
- 整个过程没有任何 Agent 生成的 HTML/JS 被直接执行,安全边界非常清晰。
它和我们的组件库/设计系统的关系大致是:
- 组件库 / 设计系统:定义「有哪些组件」「长什么样」「行为如何」;
- A2UI:定义「Agent 怎么用 JSON 把这些组件拼起来」;
- Agent:在具体对话里,根据上下文动态生成 A2UI JSON。
协议里的几个关键元素
1)声明式 JSON + surfaceUpdate
A2UI 不传 DOM,只传「界面蓝图」。最常见的消息之一是 surfaceUpdate,用来声明某个界面区域(surface)的组件结构:
{
"surfaceUpdate": {
"surfaceId": "task-form",
"components": [
{
"id": "root",
"component": {
"Column": {
"children": { "explicitList": ["title", "submit"] }
}
}
},
{
"id": "title",
"component": {
"Text": { "text": { "literalString": "创建任务" } }
}
},
{
"id": "submit",
"component": {
"Button": {
"child": "submit_text",
"action": { "name": "submit_task" }
}
}
}
]
}
}
这里需要注意两点:
- 这是纯数据,客户端可以先缓冲,不急着渲染;
- 所有组件都用
id标识,不是嵌套树,而是扁平列表。
这就是 A2UI 常说的 邻接表 模型:组件之间靠 id/children.explicitList 互相引用。对 LLM 来说更友好,可以增量生成,也方便做局部更新。
2)组件目录 Catalog:安全白名单
为了防止 Agent 想出一些我们不认识的组件,A2UI 引入了 Catalog 白名单:
- 客户端维护一份组件目录,例如
bytedesign.Text、bytedesign.Button.primary等; - Agent 在 JSON 里只能引用 Catalog 里存在的组件类型;
- 真正的实现、样式、无障碍属性全部由客户端掌控。
对我们来说,这一层其实就是「把现在的组件库+设计系统,用一个机器可读的方式暴露给 Agent」。
3)数据模型 + dataModelUpdate + BoundValue
A2UI 要求 UI 结构和数据模型分离:
surfaceUpdate负责「有哪些组件」;dataModelUpdate负责「这些组件用什么数据」。
组件属性通过 BoundValue 绑定到数据模型,例如:
{
"dataModelUpdate": {
"surfaceId": "task-form",
"path": "/task",
"contents": [
{ "key": "title", "valueString": "Q3 营销复盘" },
{ "key": "owner", "valueString": "ops_user_01" }
]
}
}
组件里写的是:
"text": { "path": "/task/title" }
实际落地时,这一层跟我们原来做 数据契约 / JSON Schema 的思路是对齐的,只是现在变成 Agent 也要遵守这份契约。
4) beginRendering :防止半成品界面闪烁
如果客户端一收到组件就马上渲染,用户会看到 UI 一块块地长出来,甚至因为依赖没齐导致报错。A2UI 用一条 beginRendering 消息做「开闸信号」:
- 客户端先缓冲
surfaceUpdate和dataModelUpdate; - 收到
beginRendering才开始首帧渲染; - 后续的增量更新再走流式刷新。
我们在客户端渲染器里实现的「事务化缓冲」也是围绕这个设计展开的,后面 Case 里会展开细节。
5) userAction :把交互结果送回 Agent
界面渲染完只是第一步,用户点了按钮、改了表单,还要反馈给 Agent。A2UI 用 userAction 统一描述这些事件,例如:
{
"userAction": {
"name": "submit_task",
"surfaceId": "task-form",
"sourceComponentId": "submit",
"context": {
"title": "Q3 营销复盘",
"owner": "ops_user_01"
}
}
}
这里需要注意:
name通常来自组件的action.name,需要我们在组件库/设计系统侧统一命名规范;context里应该是已经解析好的业务数据,而不是随便 dump 一整个 data model。
这正好对应我们在「落地策略」里提到的 事件命名规范 和 数据契约。
对现有组件库/设计系统的作用
综合来看,A2UI 在整个体系中的作用可以简单理解为:
- 上层有各种 Agent,它们通过 A2UI「说 UI」;
- 中间层是 A2UI 渲染器 + Catalog,把 JSON 翻译成真实组件树;
- 底层是我们已经在演进的「Code = Spec」组件库和 Design Token 体系。
换句话说,A2UI 把“可执行 UI 组件”这件事扩展到了 Agent 维度:Agent 不再只能吐文本,而是可以安全地复用我们的组件库,生成一套可执行、可观察、可治理的动态 UI。这就是为什么在讨论「AI 时代的组件库」时,A2UI 会成为一个绕不开的话题。
A2UI 实战 Case:从文本 Agent 到任务面板
为了更具体一点,这里用一个我们自己非常典型的场景,走一遍从「纯文本 Agent」到「A2UI + 可执行组件」的完整路径。
场景与问题:文本 Agent 做运营任务太费劲
业务场景大概是这样的:
- 运营同学在一个「智能助手」里,让 Agent 帮他创建一个运营任务,比如「帮我建一个 Q3 折扣活动的审批单」;
- 文本模式下,Agent 会一轮轮追问:活动名称?开始结束时间?负责人?适用人群?……最后再把所有字段整理成一条长消息让人确认。
踩过坑之后大家的共识是:
- 对简单问答还行,但一旦字段多、规则复杂,纯文本交互体验非常糟糕;
- 运营同学经常在第 N 条消息才发现某个字段填错,又得从头来一遍;
- 我们明明已经有一整套后台表单组件库,却没法在 Agent 场景里复用。
所以这次尝试的目标很直接:让 Agent 生成一个「任务表单」界面,用户一次性把信息填好,Agent 负责写回系统。
尝试:接入 A2UI,把对话拉平成一次 GUI 操作
整体链路拆开看,大致分四步。
Step 1:Agent 生成表单的 surfaceUpdate
当用户输入「帮我创建一个 Q3 折扣活动审批单」时,Agent 不再回一长段提示语,而是生成一个表单界面的蓝图:
{
"surfaceUpdate": {
"surfaceId": "campaign-form",
"components": [
{
"id": "root",
"component": {
"Column": {
"children": { "explicitList": ["title", "name", "time", "owner", "submit"] }
}
}
},
{
"id": "title",
"component": {
"Text": { "text": { "literalString": "创建折扣活动审批单" } }
}
},
{
"id": "name",
"component": {
"TextField": {
"label": { "literalString": "活动名称" },
"value": { "path": "/campaign/name" }
}
}
},
{
"id": "submit",
"component": {
"Button": {
"child": "submit_text",
"action": { "name": "submit_campaign" }
}
}
}
]
}
}
这里我们约定所有组件类型都来自内部 Catalog,比如 bytedesign.TextField、bytedesign.Button.primary,Agent 只需要记住这些名字。
Step 2:Agent 用 dataModelUpdate 填充默认值
接下来 Agent 会根据前文对话,为表单填一些建议值,例如活动名称、默认负责人:
{
"dataModelUpdate": {
"surfaceId": "campaign-form",
"path": "/campaign",
"contents": [
{ "key": "name", "valueString": "Q3 新用户折扣活动" },
{ "key": "owner", "valueString": "ops_lead_01" }
]
}
}
这一步完全复用我们已有的数据契约:/campaign 的 Schema 本来就存在,只是现在换成 Agent 在填充。
Step 3:客户端渲染器 + Catalog 把 JSON 变成真实界面
客户端拿到这两条消息后,做的事情其实很「老派前端」:
- A2UI 渲染器把
surfaceUpdate解析成内部的虚拟节点结构; - 通过 Catalog 把
TextField映射到我们组件库里的对应组件; - 根据 BoundValue 把
/campaign/name等路径绑定到本地状态管理里; - 收到
beginRendering后,一次性渲染出完整表单。
这里需要注意的是:我们没有让渲染器直接「信任」 Agent 给的任何细节,所有样式、布局和交互行为,仍然全部来自组件库本身。
Step 4:用户提交表单, userAction 回到 Agent
当运营同学填完表单,点击「提交」按钮时,客户端会发送一条 userAction:
{
"userAction": {
"name": "submit_campaign",
"surfaceId": "campaign-form",
"sourceComponentId": "submit",
"context": {
"campaign": {
"name": "Q3 新用户折扣活动",
"owner": "ops_lead_01"
}
}
}
}
Agent 收到这条消息后,调用内部系统 API 真正创建活动,并再通过 A2UI 返回一个「创建成功」的确认卡片。
踩坑与修复:三件印象很深的事
这条链路跑通以后,真正花时间的是各种「细节坑」,这里挑三件对工程影响最大的说一下。
- 邻接表 更新次序不对,界面里长出“幽灵节点”
一开始我们让 Agent 流式更新 UI:先把某个节点的 children 换成新 ID,再补发新组件定义。结果某些情况下渲染器会遇到「引用了一个还没定义的组件」,界面上就出现一个空壳节点。最后的做法是:
- 首帧渲染严格依赖
beginRendering,之前所有消息只缓冲不渲染; - 增量更新走「小事务」:在渲染器里做 30–50ms 的合并窗口,按依赖顺序批量应用
surfaceUpdate,而不是来一条改一条。
- BoundValue 路径漂移导致数据“抽空”
数据模型从 /campaign/name 升级成 /campaign/meta/name 后,如果老 Agent 还在发送旧路径,界面上会出现一片空。我们最后是靠两层兜底:
- 所有
dataModelUpdate和绑定路径都走 JSON Schema 校验; - 在客户端加了一层 Path 兼容表,把旧路径透明映射到新路径,同时把命中次数打点上报,用来追踪还没升级的 Agent。
- Catalog 版本不兼容,老 Agent 用不上新能力
Button 从 label 改到 text 后,如果直接把旧属性删掉,老 Agent 就直接报错了。这里的经验是:
- Catalog 必须版本化,Agent 在
beginRendering里声明自己期望的 Catalog 版本; - 组件实现里给旧属性留一个「废弃窗口」,比如同时支持
label和text,但对label打 warning; - 在监控里统计这些 warning,反向提醒业务方「还有哪些 Agent 需要改」。
小结:体验与工程侧都得到了什么
从这次 Case 的结果看:
- 对用户来说,从「十几轮文本确认」变成「一张表单+一次确认卡片」,平均交互轮次直接砍半;
- 对前端来说,我们没有多维护一套「Agent 专用组件」,完全复用现有组件库和 Token 体系;
- 对平台来说,A2UI 把 UI 这层变成了一条可观测的协议:消息可审计、数据有 Schema、组件有 Catalog,这为后面做灰度、限流、风控都打下了基础。
后面的章节会从策略、演进路径和挑战几个维度,把这次实践抽象成一套可以在更多业务里复用的方案。
落地策略:构建下一代组件库工程体系
从理论走向实践,将“代码即规格”的理念落地,需要一套系统性的工程策略。这不仅是技术选型的问题,更涉及到流程、规范和组织文化的全面升级。以下是构建下一代组件库工程体系的五个关键策略。
1. 行为/状态的声明式建模与数据契约
为了让组件的行为可预测、可测试,我们需要摒弃命令式的 DOM 操作,转向声明式的状态管理。
- 引入 状态机:对于包含多个复杂状态的组件(如表单、下拉菜单、向导等),使用
XState或类似的库来构建形式化的状态机。状态机不仅能清晰地定义所有可能的状态、事件和转换,其定义本身即可作为文档,并能被工具可视化,极大地降低了理解和维护的复杂度。 - 建立数据契约:强制为每个组件的
props定义严格的 TypeScript 类型。对于跨语言或需要持久化的场景,可以使用 JSON Schema 来定义数据结构。这份“数据契约”是组件与外部世界沟通的基石,确保了输入的有效性,并为自动化测试和文档生成提供了依据。
2. 设计 Token 的系统化治理
当 Token 从代码中反向生成时,其治理变得至关重要,否则很容易陷入“Token 泛滥”的混乱。
- 分层与命名:建立一套清晰的 Token 命名体系,通常包括多个层级。例如,
primitive/core tokens(如color.blue.500) 定义基础色板,semantic tokens(如color.background.brand) 赋予业务含义,component-level tokens(如button.background.primary) 则针对特定组件。这种分层结构兼顾了灵活性与一致性。 - 自动化发布流:搭建一个自动化的 Token 发布流水线。当组件库代码合并到主分支后,CI/CD 流程应能自动触发 Token 抽取、版本号提升、生成多格式(CSS/JS/JSON)产物,并发布到 npm 或其他包管理平台。同时,流水线还应能将更新后的 Token 推送至 Figma 等设计工具。
3. “活体”文档与示例 (Live Docs)
在“代码即规格”的范式下,文档不应再是独立于代码的手册,而应是与代码共生的“活体演示”。
- 以代码为单一事实来源:利用
Storybook、Docusaurus或自定义的文档站点,直接从组件源代码(包括 JSDoc 注释、类型定义)中自动生成文档。这意味着对代码的任何修改都会即时反映在文档中。 - 交互式示例:文档中的每个示例都应该是可交互的“活体”组件,而非截图。读者可以直接在文档页面中修改
props、触发事件、观察状态变化,从而获得最直观的理解。这正是Storybook的核心价值所在。
4. 拥抱生成式能力并建立“护栏”
生成式 AI 是新工作流的催化剂,但其产出质量的不确定性也带来了风险。我们需要在拥抱效率的同时,建立起可靠的“护栏”。
- 分阶段引入:可以从低风险的场景开始,例如,使用 AI 生成组件的初始模板代码或 Storybook 故事。对于核心业务组件,AI 的角色更多是作为“编码助手”,而非“最终决策者”。
- 自动化 质量门禁:在 CI 流程中集成严格的质量门禁。所有 AI 生成或人工编写的代码,都必须通过 Lint 规则检查、单元测试、类型检查和端到端测试,才能被合入。视觉回归测试(Visual Regression Testing)则可以有效防止意外的 UI 变更。
- 人工评审:代码评审(Code Review)在 AI 时代依然不可或缺。评审的重点应从像素级的样式对齐,转向对组件行为逻辑、状态管理和数据契约的审查。
5. 组织协作与角色重塑
技术和流程的变革,最终需要人的适应与协作。
设计师与工程师的融合: 新的工作流打破了设计师和工程师之间的壁垒。UX 设计师的角色将逐渐向 系统行为设计师 (System Behavior Designer) 和 UI 架构师 (UI Architect) 演进。他们不再仅仅关注视觉像素,而是更多地思考组件的结构、状态逻辑和动态行为。工程师则需要更早地参与到设计过程中,用技术视角评估可行性,并共同定义组件的“规格”。这种“你中有我,我中有你”的深度协作是新范式成功的关键。
演进路径:分阶段迈向 Code-First 设计系统
行动清单(按优先级)
为了让上面的思路真的落下地,我们可以按 P0 / P1 / P2 把事情拆开:
-
P0:先把链路打通
- 在 Web 端落一个 MVP 级 A2UI 渲染器(建议 React/Lit 任选其一),能完整跑通
surfaceUpdate/dataModelUpdate/beginRendering/deleteSurface/userAction。 - 定义一份对齐 A2UI v0.8 的基础 Catalog,覆盖
Text、Button、TextField、Row/Column、Card、Image等 8–10 个高频组件。 - 用 TypeScript 类型 + JSON Schema 把消息和数据模型都描述清楚,在服务端接入 Schema 校验,禁止「裸 JSON」直达客户端。
- 在 Web 端落一个 MVP 级 A2UI 渲染器(建议 React/Lit 任选其一),能完整跑通
-
P1:把工程护栏补齐
- 在 CI 里加两道门:一是 A2UI JSON 的 Schema 校验,二是 Catalog Lint(引用的组件/属性必须存在于对应版本 Catalog)。
- 由前端架构师 + UX 共同沉淀一份「数据绑定路径规范」和「userAction 命名规范」,避免不同 Agent 自己造一套方言。
- 为关键渲染路径和错误场景埋点,打通日志与告警,确保一旦 Agent 生成了有问题的 UI,我们能第一时间发现。
-
P2:和设计系统深度融合
- 做一版从组件实现到 Design Token 的反向抽取工具,把 A2UI 场景也纳入 Token 治理链路。
- 把组件库文档升级成 Live Docs:所有示例都跑在真实 A2UI 渲染器之上,既是 demo,也是回归测试。
- 预研 Flutter / Native 渲染器的最小可行方案,为后续真正做到「一次协议,多端落地」打基础。
为了让上面的思路真的落下地,我们可以按 P0 / P1 / P2 把事情拆开:
-
P0:先把链路打通
- 在 Web 端落一个 MVP 级 A2UI 渲染器(建议 React/Lit 任选其一),能完整跑通
surfaceUpdate/dataModelUpdate/beginRendering/deleteSurface/userAction。 - 定义一份对齐 A2UI v0.8 的基础 Catalog,覆盖
Text、Button、TextField、Row/Column、Card、Image等 8–10 个高频组件。 - 用 TypeScript 类型 + JSON Schema 把消息和数据模型都描述清楚,在服务端接入 Schema 校验,禁止「裸 JSON」直达客户端。
- 在 Web 端落一个 MVP 级 A2UI 渲染器(建议 React/Lit 任选其一),能完整跑通
-
P1:把工程护栏补齐
- 在 CI 里加两道门:一是 A2UI JSON 的 Schema 校验,二是 Catalog Lint(引用的组件/属性必须存在于对应版本 Catalog)。
- 由前端架构师 + UX 共同沉淀一份「数据绑定路径规范」和「userAction 命名规范」,避免不同 Agent 自己造一套方言。
- 为关键渲染路径和错误场景埋点,打通日志与告警,确保一旦 Agent 生成了有问题的 UI,我们能第一时间发现。
-
P2:和设计系统深度融合
- 做一版从组件实现到 Design Token 的反向抽取工具,把 A2UI 场景也纳入 Token 治理链路。
- 把组件库文档升级成 Live Docs:所有示例都跑在真实 A2UI 渲染器之上,既是 demo,也是回归测试。
- 预研 Flutter / Native 渲染器的最小可行方案,为后续真正做到「一次协议,多端落地」打基础。
对于大多数已有成熟设计系统的团队而言,一夜之间切换到 Code-First 模式既不现实也无必要。一个循序渐进的、分阶段的演进路径更为稳妥。
第一阶段:试点与工具链建设
-
目标:验证新工作流的可行性,搭建基础工具链。
-
关键里程碑:
- 选择一个独立的、非核心的业务场景或新项目作为试点。
- 搭建基础的 Token 自动化流程:能够从试点项目的代码中抽取一部分核心 Token,并发布。
- 引入 Storybook,为试点组件编写“活体”文档。
- 初步尝试使用 AI 工具辅助生成组件代码,并评估其效果。
-
易踩的坑:
- 过度设计:试图在一开始就建立一个大而全的 Token 体系。应从最基础的颜色、字体开始。
- 忽视协作:仅由工程师推动,未能让设计师早期参与,导致后续推广困难。
第二阶段:双轨运行与规范共存
-
目标:扩大 Code-First 模式的应用范围,同时保持与现有设计系统的兼容。
-
关键里程碑:
- 建立正式的 Token 治理规范,并开始将公司级的核心设计规范(如品牌色)以 Code-First 的方式管理。
- 对于新开发的组件,强制要求遵循 Code-First 模式;对于存量组件,逐步进行改造或保持现状。
- 实现 Token 从代码到 Figma 的单向同步,让设计师开始在 Figma 中使用由代码生成的 Token。
-
易踩的坑:
- 同步冲突:在双轨制下,容易出现 Figma 手动修改与代码同步之间的冲突。需要明确规定,由代码生成的 Token 在 Figma 中是“只读”的。
- 维护成本:同时维护两套系统(旧的 Figma 组件库和新的 Code-First 组件库)会带来额外的维护成本。需要有明确的计划,逐步淘汰旧系统。
第三阶段:全面切换与文化沉淀
-
目标:将 Code-First 确立为团队唯一的、权威的工作模式。
-
关键里程碑:
- 绝大部分 UI 组件都已通过 Code-First 模式进行管理。
- “代码即规格”的理念深入人心,设计师和工程师能够在新工作流中顺畅协作。
- 设计系统的文档、社区和贡献流程完全基于代码库构建。
- AI 生成能力被成熟地集成到日常工作中,并有完善的质量保障体系。
-
易踩的坑:
- 技能短板:团队成员(尤其是设计师)可能缺乏驾驭新工具和新流程所需的技能。需要提供相应的培训和支持。
- 僵化执行:将 Code-First 视为一成不变的教条,而忽视了在特定场景下(如快速原型验证)Figma 的价值。应保持灵活性,让工具服务于目标,而非反之。
挑战与缓解策略
在迈向 Code-First 的旅程中,必然会遇到一系列挑战。正视它们并提前准备好应对策略,是成功转型的关键。
-
生成质量与一致性:AI 生成的代码可能风格不一、质量参差不齐。
- 缓解:通过严格的 Lint 规则、代码格式化工具、以及预设的“优良”代码模板来约束 AI 的输出。将 AI 定位为“助手”而非“作者”,并强调人工评审的重要性。
-
Token 泛滥与版本治理:随着系统演进,Token 数量可能爆炸性增长,版本管理变得复杂。
- 缓解:建立严格的 Token 准入机制和废弃流程。使用语义化版本控制(Semantic Versioning),并为 Token 的发布提供清晰的变更日志(Changelog)。
-
性能与 可观测性:高度动态和组合化的组件可能带来性能开销,同时其内部状态也更难追踪。
- 缓解:在组件设计之初就考虑性能,避免不必要的重渲染。利用
React Profiler等工具进行性能分析。通过在状态机中内置日志和事件派发,提升组件的可观测性。
- 缓解:在组件设计之初就考虑性能,避免不必要的重渲染。利用
-
数据隐私与安全:当组件与 AI 模型或后端服务深度绑定时,需要特别关注数据隐私和安全问题。
- 缓解:明确数据边界,组件本身不应处理敏感数据。与后端服务的通信必须遵循严格的安全协议。所有与外部模型的交互都应在受控的环境中进行。
A2UI 协议落地的额外挑战
在上面的 Case 里,其实已经暴露出 A2UI 几类典型工程问题,这里再从「模式」角度归纳一下:
- 邻接表 的更新时序:如果 Agent 先改 children,再补发新组件定义,渲染器就有可能引用到尚未定义的组件,出现「幽灵节点」。缓解思路是:用
beginRendering保证首帧原子性,在渲染器里为增量更新增加 30–50ms 的小事务窗口,按依赖顺序批量应用surfaceUpdate。 - 数据模型和路径演进:当数据 Schema 变更时,老 Agent 还在使用旧路径,容易导致 BoundValue 绑定失败,界面出现空洞。缓解思路是:给数据模型做 JSON Schema 版本化,在客户端加 Path 兼容表,并对兼容命中打点,帮助定位需要升级的 Agent。
- Catalog 版本兼容:组件属性/能力升级后,老 Agent 仍然在用旧属性,轻则体验降级,重则直接渲染失败。缓解思路是:Catalog 版本化 + 能力画像,在组件实现里给旧属性留出明确的废弃窗口,通过日志与告警驱动业务方完成迁移。
这些挑战本质上都是「协议演进」问题,和我们在组件库、API 演进里遇到的是一类东西,只是现在对象换成了 Agent。
仍然保留的两个开放问题
即便把上面的护栏都补齐,还有两件事目前只能算「在探索中」。
-
多 Agent 下的 UI 冲突仲裁
- 在多 Agent mesh 场景里,很容易出现多个 Agent 想同时更新同一个 surface 的情况:一个 Agent 想把表单变成只读,另一个 Agent 想再塞一个推荐列表进来。简单的 last-write-wins 大概率会出问题,更合理的做法可能需要:
- 在 orchestrator 层做「UI 所有权」和「优先级」管理;
- 把 UI 更新建模成显式的状态机或 CRDT,支持合并与回滚;
- 甚至为 UI 层单独引入一个「仲裁 Agent」。
- 这些方向目前还没有行业统一解,我们也只是有一些初步尝试。
-
离线 / 弱网场景下的缓存与回放
- A2UI 天生是流式协议,一旦网络不稳定,很容易出现「用户操作已经发生,但 Agent 还没收到」的分叉。我们现在只做了非常保守的策略:
- 在客户端缓存
userAction队列,在网络恢复后按顺序重放; - 给每一次渲染和每一条
userAction编号,避免重复执行; - 一旦发现本地状态和服务端状态差异过大时,优先提示用户「需要刷新界面」。
- 但更优雅的方案(比如在 UI 层引入类似 event sourcing / log compaction 的机制),目前还在调研阶段。
这些问题没有标准答案,但正是它们,决定了我们能不能把「A2UI + 可执行组件」从 demo 做成真正可靠的平台。
结语:拥抱变化,重塑未来
从 Figma 组件到可执行 UI 组件,从“设计驱动开发”到“代码驱动设计”,我们正在经历的不仅仅是一次工具或流程的升级,而是一场关于“创造”本身的深刻思考。在这场变革中,前端组件库不再仅仅是静态的 UI 资产库,它正在进化为一个可运行、可交互、自我演进的 UI 架构系统。
这要求我们打破传统的角色分工,鼓励设计师和工程师成为彼此的“翻译官”和“共创者”。设计师需要更懂代码的逻辑,工程师需要更懂设计的意图。而 AI,正是催化这一融合的强大力量。
前路充满挑战,但也同样充满机遇。对于每一位前端工程师、设计系统工程师和产品设计师而言,现在正是最好的时机,去学习新的工具,拥抱新的流程,并在这场波澜壮阔的范式迁移中,重新定义我们的工作方式,共同塑造人机交互的未来。