Figma 到 React 组件:Design Token 与 AST 如何划分自动化边界
从 Figma 直接生成 React 组件很诱人,但布局、Token 映射和 Props 契约不应全部交给模型猜。下面把确定性转换留给规则和 AST,只让模型处理注释与用例等开放任务。判断方案是否省工,应以实际 PR 的返工记录为准。
1. 失败复盘:全自动大模型生成组件的三大问题
仔细剖析当时生成的组件库代码,导致自动化实验崩塌的核心矛盾有三点:
2. 演进方案:确定性 AST 规则引擎 + 局部 LLM 增量辅助
在吸取教训后,可以重构组件库自动化流水线。核心思想是“确定性归确定性,概率归概率”:
- 布局解析、Design Tokens 映射、TypeScript 类型声明等强规范部分,全部交给确定的 Figma REST API + Style Dictionary + AST Transpiler 链条处理;
- 大模型仅在局部负责补充组件注释、生成 Mock 测试数据以及将自然语言转化成复杂的组件使用 Example。
3. 核心实现:基于 Node.js 的确定性 Design Token 与代码转换引擎
以下是重构后的组件自动化转换器核心逻辑。代码展示了如何使用确定性的节点扫描与 Style Dictionary 规范,将 Figma 属性精准转换为符合工程规范的代码:
import fs from 'fs';
import path from 'path';
interface FigmaNode {
id: string;
name: string;
type: string;
fills?: Array<{ type: string; color?: { r: number; g: number; b: number; a: number } }>;
children?: FigmaNode[];
style?: Record<string, any>;
}
interface DesignTokenMap {
[colorHex: string]: string;
}
export class ComponentAutoGenerator {
private tokenMap: DesignTokenMap;
constructor(tokenMap: DesignTokenMap) {
this.tokenMap = tokenMap;
}
/**
* 将 RGB(0~1) 转换为标准的 HEX 颜色格式
*/
private rgbToHex(r: number, g: number, b: number): string {
const toHex = (val: number) => {
const hex = Math.round(val * 255).toString(16);
return hex.padStart(2, '0');
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
}
/**
* 确定性扫描 Figma 树节点,提取并替换为 Design Token
*/
public parseFigmaNodeToCssVars(node: FigmaNode): Record<string, string> {
const cssStyles: Record<string, string> = {};
// 1. 解析背景色与 Token 映射
if (node.fills && node.fills.length > 0) {
const fill = node.fills[0];
if (fill.type === 'SOLID' && fill.color) {
const hex = this.rgbToHex(fill.color.r, fill.color.g, fill.color.b);
// 强行阻断硬编码 HEX,查找映射的 Design Token
const matchedToken = this.tokenMap[hex] || `/* Warning: 未定义的颜色 ${hex} */ ${hex}`;
cssStyles['background-color'] = matchedToken;
}
}
// 2. 确定性推导 Flexbox 布局 semantics
if (node.type === 'FRAME' || node.type === 'COMPONENT') {
cssStyles['display'] = 'flex';
cssStyles['flex-direction'] = node.style?.layoutMode === 'VERTICAL' ? 'column' : 'row';
if (node.style?.itemSpacing) {
cssStyles['gap'] = `${node.style.itemSpacing}px`;
}
}
return cssStyles;
}
/**
* 生成干净、语义化的 React TSX 模板
*/
public generateReactComponent(componentName: string, styles: Record<string, string>): string {
const styleString = Object.entries(styles)
.map(([key, val]) => ` ${key}: '${val}'`)
.join(',\\n');
return `import React from 'react';
export interface ${componentName}Props {
children?: React.ReactNode;
className?: string;
onClick?: () => void;
}
/**
* 确定性流水线生成的组件骨架 – ${componentName}
*/
export const ${componentName}: React.FC<${componentName}Props> = ({
children,
className = '',
onClick
}) => {
return (
<div
className={className}
onClick={onClick}
style={{
${styleString}
}}
>
{children}
</div>
);
};
`;
}
}
4. 方案对比:纯大模型生成 vs 确定性 AST 规则+局部 LLM
经过重构后,可以将之前的失败方案与当前的新架构在组件自动化生成场景下进行对比压测:
| Design Token 命中率 | 记录纯 Vision LLM 全自动生成(失败实验) | 记录确定性 AST 规则 + 局部 LLM(现行方案) | 解决样式断层问题 |
| 平均 DOM 嵌套层级 | AST 统计纯 Vision LLM 输出 | AST 统计规则 + 局部 LLM 输出 | 比较分布而非单个样例 |
| 代码 Lint 通过率 | 记录纯 Vision LLM 全自动生成(失败实验) | 记录确定性 AST 规则 + 局部 LLM(现行方案) | 无须人工大量重构 |
| 单组件生成开销 | 记录纯 Vision LLM 全自动生成(失败实验) | 记录确定性 AST 规则 + 局部 LLM(现行方案) | 记录差异分析 |
| PR 前端二次修改率 | 记录纯 Vision LLM 全自动生成(失败实验) | 记录确定性 AST 规则 + 局部 LLM(现行方案) | 研发效率真正提升 |
5. 总结与反思
那次失败的自动化实验,给该工程团队留下了非常珍贵的资产:

