Storybook 组件文档:UI 组件隔离开发与展示

FreeGuideOnline 最新 2026-06-15

为什么需要 Storybook?——UI 组件隔离开发与展示

当项目越来越大,组件数量急剧增长,你可能会遇到这些痛点:

  • 想查看某个按钮在「主要/次要/禁用/加载中」状态的样子,必须反复切换代码或手动触发状态。
  • 组件与页面强耦合,单独调试一个组件就需要启动整个应用。
  • 设计师或产品经理想验收组件,却没有一个可以即时查看的可视化目录。
  • 新同事接手项目,面对几十个组件不知从何看起,缺少一份“活着的”组件使用手册。

Storybook 正是为了解决这些问题而生。 它是一个前端组件开发环境与文档生成工具,能够在项目之外独立运行,让你可以:

  • 在隔离环境中开发、调试 UI 组件。
  • 为每个组件编写多个“故事”(Story),覆盖各种状态与交互。
  • 自动生成可交互的组件文档,供团队共享。
  • 与视觉回归测试、单元测试、无障碍检查工具集成。

本教程将带你从零开始,掌握 Storybook 的安装、故事编写、文档生成、插件配置与部署,最终打造一套专业、可维护的组件库文档体系。


环境准备与安装

前置条件

  • 已有基于 React、Vue、Angular、Svelte 等现代框架的项目(或能创建新项目)。
  • Node.js ≥ 16(推荐 18+),包管理器使用 npm、yarn 或 pnpm。

在现有项目中安装 Storybook

在项目根目录执行以下命令,Storybook 会自动识别你的框架并安装依赖:

npx storybook@latest init

该命令会完成:

  1. 检测项目所使用的框架(React、Vue 等)。
  2. 安装 @storybook/react(或对应框架包)及必要依赖。
  3. 创建默认配置文件(.storybook/main.js)和一个示例 Story。
  4. package.json 中添加 storybookbuild-storybook 脚本。

安装完成后,启动 Storybook:

npm run storybook

浏览器会打开 http://localhost:6006,你将看到内置的示例组件及其文档。

项目结构概览

初始化后的关键目录与文件:

.storybook/
  main.js        # 主配置:故事路径、插件、Webpack/Vite 配置
  preview.js     # 全局参数、装饰器、全局样式
src/
  stories/       # 示例故事文件(可自定义路径)

编写你的第一个 Story

Story 就是一个函数,返回组件在特定状态下的渲染结果。Storybook 通过“故事”来描述组件的各种表现。

组件文件 (以 React 为例)

假设我们有一个简单的 Button 组件:

// src/components/Button.jsx
export const Button = ({ label, primary, disabled, size, onClick }) => {
  const mode = primary ? 'btn-primary' : 'btn-secondary';
  return (
    <button
      className={`btn ${mode} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {label}
    </button>
  );
};

创建 Story 文件

在组件同级目录或 stories 目录下创建 Button.stories.jsx

import { Button } from './Button';

export default {
  title: '通用/Button',
  component: Button,
  // 定义组件参数,会与控件面板联动
  argTypes: {
    label: { control: 'text' },
    primary: { control: 'boolean' },
    size: {
      control: 'select',
      options: ['small', 'medium', 'large'],
    },
    disabled: { control: 'boolean' },
  },
};

// 模板:返回组件渲染结果
const Template = (args) => <Button {...args} />;

// 默认状态故事
export const Default = Template.bind({});
Default.args = {
  label: '按钮',
  primary: false,
  size: 'medium',
  disabled: false,
};

// 主要按钮
export const Primary = Template.bind({});
Primary.args = {
  ...Default.args,
  primary: true,
};

// 禁用态
export const Disabled = Template.bind({});
Disabled.args = {
  ...Default.args,
  disabled: true,
};

保存后,Storybook 侧边栏立即出现“通用/Button”分组,下方是 Default、Primary、Disabled 三个故事。点击即可在右侧 Canvas 面板预览,并在 Controls 面板动态修改属性。


Story 格式详解:CSF 3.0 声明式写法

Storybook 7 开始推荐 CSF 3.0(Component Story Format)写法,更加简洁与声明式。上面的例子可以改写为:

export default {
  title: '通用/Button',
  component: Button,
};

export const Default = {
  args: {
    label: '按钮',
    primary: false,
    size: 'medium',
    disabled: false,
  },
};

export const Primary = {
  args: {
    ...Default.args,
    primary: true,
  },
};

CSF 3.0 不再需要 Template.bind({}),每一个故事就是一个对象,args 直接挂载在故事对象上。当你需要使用多个模板时,可以借助 render 函数。

何时使用 render?
当不同故事需要不同渲染逻辑(例如不同标签、容器装饰)时,可以为单个故事定义 render

export const WithIcon = {
  args: { label: '搜索', icon: 'search' },
  render: (args) => (
    <div style={{ background: '#333', padding: 20 }}>
      <Button {...args} />
    </div>
  ),
};

交互式故事:play 函数与用户操作模拟

借助 @storybook/addon-interactions,你可以为故事添加自动化交互操作,像编写测试一样验证组件行为。

安装交互插件

npm install @storybook/addon-interactions --save-dev

.storybook/main.js 中添加:

export default {
  addons: ['@storybook/addon-interactions'],
};

为故事添加 play 函数

play 函数接收一个包含 canvasElement 的对象,可使用 Testing Library 的工具在 Canvas 中执行交互:

import { userEvent, within } from '@storybook/testing-library';

export const ClickInteraction = {
  args: { label: '点击我' },
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    const button = await canvas.getByRole('button');
    await userEvent.click(button);
    // 断言:按钮点击后移除了自身(示例)
    expect(button).not.toBeInTheDocument();
  },
};

执行 play 函数时,交互步骤会记录在 Interactions 面板中,你可以逐步回放,直观地调试。


用 MDX 制作组件文档页

Storybook 支持 MDX,允许你将 Markdown 文档与实时渲染的 Story 编织在一起。

1. 安装 MDX 支持(通常默认支持)

确保 .storybook/main.js 中有 '@storybook/addon-docs'

2. 创建 MDX 文档文件

在组件旁新建 Button.mdx

import { Meta, Story, Canvas, ArgTypes } from '@storybook/blocks';
import { Button } from './Button';

<Meta title="通用/Button" component={Button} />

# Button 按钮

按钮用于触发即时操作。我们提供了多种样式和状态。

## 基本按钮

<Canvas>
  <Story name="基本" args={{ label: '按钮' }}>
    {(args) => <Button {...args} />}
  </Story>
</Canvas>

## 属性

<ArgTypes story="基本" />

MDX 页面会自动出现在侧边栏“通用/Button”下,点击后既能看到说明文档,又能直接与组件交互。


控件类型与参数扩展

通过 argTypes 你可以精细定义每个属性在 Controls 面板的表现形式、描述、分类等。

常用 Control 类型:

类型 说明
text 文本输入框
boolean 切换开关
number 数字输入,可配合 minmaxstep
select 下拉选择,options 定义可选项
radio / inline-radio 单选按钮组
check / inline-check 多选按钮组
color 颜色拾取器
date 日期选择器
object / array JSON 编辑器

示例:让 size 属性以单选按钮展示,并添加描述与表格分类:

export default {
  title: '通用/Button',
  component: Button,
  argTypes: {
    size: {
      control: 'radio',
      options: ['small', 'medium', 'large'],
      description: '按钮尺寸',
      table: {
        category: '样式',
        defaultValue: { summary: 'medium' },
      },
    },
  },
};

装饰器与全局配置

全局装饰器(为每个故事包裹外层)

.storybook/preview.js 中可以为所有故事应用全局样式、路由、状态容器等:

import { ThemeProvider } from '../src/ThemeContext';
import '../src/index.css'; // 全局样式

export const decorators = [
  (Story) => (
    <ThemeProvider defaultTheme="light">
      <Story />
    </ThemeProvider>
  ),
];

组件/故事级装饰器

在 meta 或单一 Story 中也可单独使用 decorators

export default {
  title: '通用/Button',
  component: Button,
  decorators: [
    (Story) => (
      <div style={{ margin: '3em' }}>
        <Story />
      </div>
    ),
  ],
};

插件生态:扩展 Storybook 的能力

Storybook 的强大来自其插件体系。以下为核心必装插件:

插件包名 功能
@storybook/addon-essentials 包含 Controls、Actions、Viewport、Backgrounds、Toolbars、Outline 等
@storybook/addon-a11y 无障碍检查
@storybook/addon-links 故事间导航链接
@storybook/addon-interactions 交互测试与回放
@storybook/addon-mdx-gfm MDX 中使用 GitHub 风格 Markdown
@storybook/addon-designs 在故事中嵌入 Figma 设计稿
@storybook/addon-storyshots / @storybook/test-runner 截图与快照测试

安装示例:

npm install @storybook/addon-a11y --save-dev

.storybook/main.js 中注册:

module.exports = {
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-a11y',
    '@storybook/addon-interactions',
  ],
};

安装后,工具栏会多出“Accessibility”按钮,检测颜色对比度、ARIA 属性等。


自动化测试与持续集成

交互与断言测试

通过 play 函数配合 expect,故事本身就可以成为测试用例。执行测试时,使用 Storybook Test Runner:

npm install @storybook/test-runner --save-dev

运行:

npx test-storybook

它会启动 Storybook,按顺序执行每个故事的 play 函数,并报告结果。

视觉回归测试

借助 Chromatic(Storybook 官方视觉测试服务)或 Percy,你可以自动截取每个故事并与上次构建比较,发现像素级差异。

简单上手 Chromatic:

  1. 注册 Chromatic 并获取项目 token。

  2. 安装依赖:

    npm install chromatic --save-dev
    
  3. 运行:

    npx chromatic --project-token=你的token
    

第一次上传作为基线,之后每次 PR 变化都会产生快照对比。


与不同框架的集成要点

Storybook 官方支持 React、Vue、Angular、Svelte、Web Components 等。约定如下:

  • React:可直接使用 JSX,组件 props 即 args。

  • Vue:需要绑定 props 与 events;事件名使用 on 前缀(如 onClick 映射 click 事件)。CSF 举例:

    export default {
      title: 'Example/MyVueButton',
      component: MyButton,
      argTypes: {
        onClick: { action: 'clicked' },
      },
    };
    export const Default = {
      render: (args) => ({
        components: { MyButton },
        setup() { return { args }; },
        template: '<MyButton v-bind="args" />',
      }),
    };
    
  • Angular:使用 component 自动从 Angular 组件提取 inputsoutputs,直接传 args 即可。

  • SvelteWeb Components 类似,主要差异在于 args 与事件绑定的写法。


构建与部署 Storybook 静态站点

完成所有故事编写后,将其构建为纯静态文件,即可部署到任何静态托管平台。

构建命令:

npm run build-storybook

输出目录默认为 storybook-static。你可以直接通过 npx http-server storybook-static 本地预览。

常用部署目标:

  • GitHub Pages:将 storybook-static 推送到 gh-pages 分支,或在 CI 中使用 actions-gh-pages
  • Netlify / Vercel:指定构建命令 build-storybook 和发布目录 storybook-static
  • 企业内部服务器:直接放置静态文件。

示例 npm 脚本一键部署到 GitHub Pages(需先安装 gh-pages):

"deploy-storybook": "build-storybook && gh-pages -d storybook-static"

最佳实践与常见问题

组织 Story 的命名规则

  • 使用路径式分组title: '组件/Button''组件/表单/Input',自动生成折叠侧边栏。
  • 子组件可以放在同一 Story 文件中,使用 subcomponents 属性(需插件支持)或独立 story 并互相链接。

保持 Story 纯净

  • 每个 Story 应该只渲染一个组件,避免无意中引入复杂的页面布局。
  • 使用装饰器提供所需上下文(路由、主题)。
  • 如果需要展示多个相关组件(如 ListItemList 内),用 decorators 包裹。

动态参数与复杂数据

对于数组、对象参数,在 args 中直接设置,并在 control: 'object' 下可编辑。若数据量很大,可以提供一个固定数据集的故事,并通过 Controls 快速验证不同情况。

处理样式问题

  • 确保在 preview.js 中导入全局 CSS、字体文件等。
  • 如果使用了 Tailwind,请确保在 .storybook/preview.js 中引入其样式文件。
  • 遇到组件渲染空白页?在 .storybook/main.js 中检查 stories 路径匹配是否正确。

避免 Story 过多导致性能下降

  • 大量 Story 会增加启动和构建时间。可以按需编写核心状态的故事,或使用 MDX 将变体集中展示。
  • 使用 play 函数注意异步等待,避免个别 Story 阻塞整个测试。

总结

通过本教程你已经掌握了:

  • 搭建 Storybook 环境,编写第一个组件故事。
  • 使用 CSF 3.0 声明式定义故事,并结合 Controls 动态调节 props。
  • 编写交互式 play 函数,让故事成为可执行的行为测试。
  • 利用 MDX 制作整合文档与实时代码的组件页面。
  • 配置核心插件,实现无障碍检查、设计稿嵌入、视觉回归测试等高级功能。
  • 构建静态文档站点并部署到云端。

Storybook 让 UI 组件开发脱离了业务的束缚,成为真正可复用、可展示、可测试的独立单元。从今天起,尝试为你项目的每个关键组件都写一个 Story,把活文档带给整个团队。