Skip to content
返回构建教程

组件生成

从模板脚手架新 UI 组件,带正确结构、测试和 Storybook story。

为什么

设计系统里每组件应有相同结构:props 接口、forwardRef、含所有变体的 Storybook story 和单元测试。手工为每新组件创建这 4 文件浪费 15 分钟且引入不一致——一些组件有测试一些没有,一些用 forwardRef 一些忘。生成器(plop、hygen 或自定义脚本)5 秒从模板产 4 文件,每次强制相同结构。这是设计系统扩到 100+ 组件不变维护噩梦的方式:结构统一,新组件入职零努力。

何时使用

有 ≥10 组件或设计系统时。搭 plop 约 1 小时。一次性组件可跳过。

如何做

1. 装 plop 创建含组件生成器的 plopfile
   - 提示:组件名、目录(atoms/molecules/organisms)、hasVariants
   - 模板:Component.tsx、Component.test.tsx、Component.stories.tsx、index.ts

2. 模板 Component.tsx 用标准结构
   - React.forwardRef、类型化 props 接口、cn() 合并 className
   - 命名 + 默认导出;含 DevTools displayName

3. 模板 Storybook story 至少含:default、with className、disabled
   - 用 args 组合让变体继承基础 args
   - 加 docsOnly story 供文档化不渲染

4. 模板测试含 render + 快照 + 行为测试
   - render(<Component />) 不应抛
   - 测关键交互(click、input)——非仅渲染

5. 接入 package.json:"gen:component": "plop component"
   - 跑它,答 3 提示,得 4 文件首次即过 lint+test

示例

// plopfile.ts
export default function (plop) {
  plop.setGenerator("component", {
    prompts: [
      { type: "input", name: "name", message: "组件名(PascalCase)" },
      { type: "list", name: "level", choices: ["atoms", "molecules", "organisms"] },
    ],
    actions: [
      { type: "add", path: "src/components/{{level}}/{{name}}/{{name}}.tsx", templateFile: "templates/Component.tsx.hbs" },
      { type: "add", path: "src/components/{{level}}/{{name}}/{{name}}.test.tsx", templateFile: "templates/Component.test.tsx.hbs" },
      { type: "add", path: "src/components/{{level}}/{{name}}/{{name}}.stories.tsx", templateFile: "templates/Component.stories.tsx.hbs" },
    ],
  });
}

// templates/Component.tsx.hbs
import { forwardRef } from "react";
export interface {{name}}Props { className?: string; }
export const {{name}} = forwardRef<HTMLDivElement, {{name}}Props>(function {{name}}({ className }, ref) {
  return <div ref={ref} className={className} />;
});
{{name}}.displayName = "{{name}}";

常见问题

  • ⚠模板与实际项目约定漂移,生成代码每次要手修。
  • ⚠只生成 .tsx 无测试或 story,组件存在但无文档无测试。
  • ⚠不处理复合组件(Select + Option)一个生成器应产多相关文件。
  • ⚠生成后忘更新桶导出(index.ts),组件不可导入。

最佳实践

  • ✓模板放仓库像其他代码一样评审变更——它们生成代码,高杠杆。
  • ✓加生成后步骤对新文件跑 lint+test,不过则失败——抓模板漂移。
  • ✓一个生成器用 'compound' 提示支持简单和复合组件,加子文件。
  • ✓生成 Storybook story 启 autodocs,组件第一天即文档化。