DevToolBox免费
博客

Storybook 8 完全指南:组件驱动的 UI 开发 (2026)

18 分钟阅读作者 DevToolBox Team

Storybook 是业界标准的 UI 组件工作台,用于在隔离环境中构建、测试和文档化组件。借助 Storybook 8,团队可以独立于业务逻辑和后端依赖开发 UI 组件,使用 play 函数运行交互测试,通过 Chromatic 捕获视觉回归,并自动生成活文档。无论使用 React、Vue、Angular 还是 Svelte,Storybook 都提供统一的组件驱动开发环境。

TL;DR

Storybook 8 是一个 UI 组件工作台,支持在隔离环境中构建、测试和文档化组件。它支持 React、Vue、Angular 和 Svelte,采用 CSF3 故事格式、args 和 controls 交互属性、play 函数交互测试、Chromatic 视觉回归、a11y 插件无障碍审计、MDX 文档和强大的插件生态。

Key Takeaways
  • Storybook 8 使用 CSF3 格式,基于对象语法,比之前的格式更简洁、类型安全且可组合。
  • Args 和 Controls 生成交互式面板,设计师和 QA 可以实时操作组件属性而无需编辑代码。
  • Play 函数允许直接在故事中编写交互测试,使用 Testing Library 模拟用户点击、输入和断言。
  • Chromatic 集成通过跨提交、浏览器和视口比较截图来捕获视觉回归。
  • a11y 插件基于 axe-core 规则对每个故事运行自动化无障碍审计,提前发现 WCAG 违规。
  • Storybook 从故事和 MDX 文件生成活文档,确保文档始终与实际组件代码同步。

什么是 Storybook 和组件驱动开发?

Storybook 是一个开源工具,提供沙盒环境用于在隔离状态下开发 UI 组件。你可以定义故事来渲染具有不同属性、状态和上下文的组件,而不需要浏览整个应用程序。

组件驱动开发(CDD)是一种自下而上构建 UI 的方法论,从原子组件开始,组合成分子组件,最终组装成有机体和页面。Storybook 是支持此工作流的主要工具。

Storybook 与替代方案对比

Storybook 在组件工作台领域占主导地位,但也存在替代方案。Ladle 是基于 Vite 的轻量级 React 替代方案。Histoire 支持 Vue 和 Svelte。然而 Storybook 拥有最大的生态系统(400+ 插件)、最多的框架支持和最强的 CI/CD 集成。

Feature Comparison: Storybook vs Alternatives
┌──────────────────┬────────────┬────────┬──────────┬──────────────┐
│ Feature          │ Storybook  │ Ladle  │ Histoire │ React Cosmos │
├──────────────────┼────────────┼────────┼──────────┼──────────────┤
│ React            │ Yes        │ Yes    │ No       │ Yes          │
│ Vue              │ Yes        │ No     │ Yes      │ No           │
│ Angular          │ Yes        │ No     │ No       │ No           │
│ Svelte           │ Yes        │ No     │ Yes      │ No           │
│ Addons           │ 400+       │ ~10    │ ~20      │ ~15          │
│ Visual Testing   │ Chromatic  │ Manual │ Manual   │ Manual       │
│ Play Functions   │ Yes        │ No     │ No       │ No           │
│ MDX Docs         │ Yes        │ No     │ Yes      │ No           │
│ CI Test Runner   │ Yes        │ No     │ No       │ No           │
│ Build Tool       │ Vite       │ Vite   │ Vite     │ Webpack      │
└──────────────────┴────────────┴────────┴──────────┴──────────────┘

为 React、Vue、Angular 和 Svelte 设置 Storybook

Storybook 8 提供零配置设置体验。init 命令自动检测框架、安装依赖并生成配置文件。Storybook 8 默认使用 Vite 构建,提供更快的启动和热更新。

React 设置

# Initialize Storybook in a React project
npx storybook@latest init

# Or create a new React project with Storybook
npx create-react-app my-app --template typescript
cd my-app
npx storybook@latest init

Vue 设置

# Initialize Storybook in a Vue 3 project
npm create vue@latest my-vue-app
cd my-vue-app
npx storybook@latest init

Angular 设置

# Initialize Storybook in an Angular project
ng new my-angular-app
cd my-angular-app
npx storybook@latest init

Svelte 设置

# Initialize Storybook in a SvelteKit project
npx sv create my-svelte-app
cd my-svelte-app
npx storybook@latest init

初始化后,Storybook 创建 .storybook 目录,包含 main.ts(构建配置)和 preview.ts(运行时配置)。

// .storybook/main.ts
import type { StorybookConfig } from "@storybook/react-vite";

const config: StorybookConfig = {
  stories: [
    "../src/**/*.mdx",
    "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
  ],
  addons: [
    "@storybook/addon-onboarding",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
    "@storybook/addon-a11y",
  ],
  framework: {
    name: "@storybook/react-vite",
    options: {},
  },
};
export default config;

// .storybook/preview.ts
import type { Preview } from "@storybook/react";

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)/i,
        date: /Date$/i,
      },
    },
  },
};
export default preview;

使用 CSF3 格式编写故事

CSF3 是 Storybook 8 中的最新故事格式。故事定义为普通对象而非函数,更简洁且可组合。每个文件有一个默认导出(meta)描述组件,命名导出定义各个故事。

CSF3 引入了故事级 args,自动生成 Controls UI,允许非开发人员交互式探索组件变体。

// src/components/Button.stories.ts
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./Button";

// Meta describes the component
const meta = {
  title: "Components/Button",
  component: Button,
  tags: ["autodocs"],
  argTypes: {
    variant: {
      control: "select",
      options: ["primary", "secondary", "danger"],
    },
    size: {
      control: "radio",
      options: ["sm", "md", "lg"],
    },
  },
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

// Each named export is a story
export const Primary: Story = {
  args: {
    variant: "primary",
    size: "md",
    children: "Click me",
  },
};

export const Secondary: Story = {
  args: {
    ...Primary.args,
    variant: "secondary",
  },
};

export const Danger: Story = {
  args: {
    ...Primary.args,
    variant: "danger",
    children: "Delete",
  },
};

export const Large: Story = {
  args: {
    ...Primary.args,
    size: "lg",
    children: "Large Button",
  },
};

Args、Controls 和 ArgTypes

Args 是在 Storybook 中定义和操作组件属性的机制。定义 args 后,Storybook 自动生成 Controls 面板。ArgTypes 提供关于 args 的元数据,可自定义控件类型、设置有效选项和定义默认值。

Controls 支持多种输入类型:文本、数字、布尔值、单选、下拉、颜色选择器、日期选择器、滑块和对象编辑器。

// src/components/Card.stories.ts
import type { Meta, StoryObj } from "@storybook/react";
import { Card } from "./Card";

const meta = {
  title: "Components/Card",
  component: Card,
  argTypes: {
    title: { control: "text", description: "Card heading" },
    description: { control: "text" },
    imageUrl: { control: "text" },
    variant: {
      control: "select",
      options: ["default", "elevated", "outlined"],
      table: {
        defaultValue: { summary: "default" },
      },
    },
    isLoading: { control: "boolean" },
    rating: {
      control: { type: "range", min: 0, max: 5, step: 0.5 },
    },
    backgroundColor: { control: "color" },
    publishedAt: { control: "date" },
    tags: { control: "object" },
    onClick: { action: "clicked" },
  },
} satisfies Meta<typeof Card>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
  args: {
    title: "Getting Started with Storybook",
    description: "Learn how to build components in isolation.",
    variant: "default",
    isLoading: false,
    rating: 4.5,
    tags: ["tutorial", "react"],
  },
};

装饰器和参数

装饰器用额外的渲染逻辑包装故事,可提供主题、路由、国际化等上下文。装饰器可在故事级、组件级或全局级应用。

参数是附加到故事的静态元数据,用于配置插件行为,如视口大小、背景色和布局模式。

// .storybook/preview.tsx - Global decorators
import type { Preview } from "@storybook/react";
import { ThemeProvider } from "../src/theme";
import { MemoryRouter } from "react-router-dom";
import "../src/globals.css";

const preview: Preview = {
  decorators: [
    // Wrap all stories with ThemeProvider
    (Story) => (
      <ThemeProvider>
        <Story />
      </ThemeProvider>
    ),
    // Wrap all stories with Router
    (Story) => (
      <MemoryRouter initialEntries={["/"]}>
        <Story />
      </MemoryRouter>
    ),
  ],
  parameters: {
    layout: "centered",
    backgrounds: {
      default: "light",
      values: [
        { name: "light", value: "#ffffff" },
        { name: "dark", value: "#1a1a2e" },
        { name: "gray", value: "#f5f5f5" },
      ],
    },
    viewport: {
      viewports: {
        mobile: { name: "Mobile", styles: { width: "375px", height: "667px" } },
        tablet: { name: "Tablet", styles: { width: "768px", height: "1024px" } },
        desktop: { name: "Desktop", styles: { width: "1440px", height: "900px" } },
      },
    },
  },
};
export default preview;

Play 函数交互测试

Play 函数是 Storybook 8 最强大的功能之一。可以使用 Testing Library 直接在故事中编写交互测试。故事加载时自动执行交互并在面板中报告结果。

Play 函数在浏览器中运行,测试实际 DOM 行为。可模拟点击、键盘输入、表单提交等。配合 test-runner 可在 CI 中自动执行。

// src/components/LoginForm.stories.ts
import type { Meta, StoryObj } from "@storybook/react";
import { within, userEvent, expect } from "@storybook/test";
import { LoginForm } from "./LoginForm";

const meta = {
  title: "Forms/LoginForm",
  component: LoginForm,
} satisfies Meta<typeof LoginForm>;

export default meta;
type Story = StoryObj<typeof meta>;

export const FilledForm: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    // Type into the email field
    await userEvent.type(
      canvas.getByLabelText("Email"),
      "user@example.com"
    );

    // Type into the password field
    await userEvent.type(
      canvas.getByLabelText("Password"),
      "securePassword123"
    );

    // Click the submit button
    await userEvent.click(
      canvas.getByRole("button", { name: /sign in/i })
    );

    // Assert the success message appears
    await expect(
      canvas.getByText("Welcome back!")
    ).toBeInTheDocument();
  },
};

export const ValidationError: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    // Submit without filling fields
    await userEvent.click(
      canvas.getByRole("button", { name: /sign in/i })
    );

    // Assert validation errors
    await expect(
      canvas.getByText("Email is required")
    ).toBeInTheDocument();
  },
};

使用 Chromatic 进行视觉回归测试

视觉回归测试通过跨提交比较组件截图来捕获意外的视觉变化。Chromatic 是 Storybook 团队构建的官方云服务。

当 Chromatic 检测到视觉变化时,会创建审查流程。审查者可批准或拒绝变更,该流程与 GitHub PR 集成。

# Install Chromatic
npm install --save-dev chromatic

# Run visual tests (first time creates baselines)
npx chromatic --project-token=YOUR_TOKEN

# In CI (GitHub Actions example)
# .github/workflows/chromatic.yml
name: Chromatic
on: push
jobs:
  chromatic:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
      - run: npm ci
      - uses: chromaui/action@latest
        with:
          projectToken: \${{ secrets.CHROMATIC_PROJECT_TOKEN }}
          # Enable TurboSnap for faster builds
          onlyChanged: true

使用 a11y 插件进行无障碍测试

a11y 插件将 axe-core 无障碍测试集成到 Storybook 中,自动对每个故事运行审计并报告违规。

可按故事或全局配置严重级别和规则覆盖。配合 play 函数可测试交互状态下的无障碍性。

# Install the a11y addon
npm install --save-dev @storybook/addon-a11y

// .storybook/main.ts - Add to addons
addons: [
  "@storybook/addon-essentials",
  "@storybook/addon-a11y",
],

// Configure a11y rules per story
export const DarkMode: Story = {
  args: { theme: "dark" },
  parameters: {
    a11y: {
      config: {
        rules: [
          // Disable a specific rule for this story
          { id: "color-contrast", enabled: false },
        ],
      },
    },
  },
};

// Combine a11y with play function
export const DropdownOpen: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    // Open dropdown so a11y addon audits the open state
    await userEvent.click(canvas.getByRole("button"));
    await expect(canvas.getByRole("listbox")).toBeVisible();
  },
};

使用 MDX 编写文档

Storybook 支持 MDX 编写富文档页面。MDX 文档可嵌入实时故事预览、组件属性表、代码片段和自定义组件。

Storybook 8 默认使用 autodocs,自动从故事和 JSDoc 注释生成文档页面。也可用自定义 MDX 文件覆盖。

// src/components/Button.mdx
import { Meta, Story, Canvas, Controls } from "@storybook/blocks";
import * as ButtonStories from "./Button.stories";

<Meta of={ButtonStories} />

# Button

The Button component is the primary interactive element
in our design system. Use it for actions and navigation.

## Usage Guidelines

- Use **Primary** for the main call to action
- Use **Secondary** for supporting actions
- Use **Danger** only for destructive actions

## Interactive Demo

<Canvas of={ButtonStories.Primary} />
<Controls of={ButtonStories.Primary} />

## All Variants

<Canvas>
  <Story of={ButtonStories.Primary} />
  <Story of={ButtonStories.Secondary} />
  <Story of={ButtonStories.Danger} />
</Canvas>

插件生态系统

Storybook 拥有最大的插件生态系统,超过 400 个社区插件。官方插件包括 Controls、Actions、Viewport、Backgrounds 等。

插件通过定义良好的 API 接入 Storybook 生命周期,可添加面板、工具栏按钮、装饰器和自定义标签。

# Essential addons (included by default)
npm install @storybook/addon-essentials
# Includes: Controls, Actions, Viewport, Backgrounds,
#   Toolbars, Measure, Outline, Docs

# Popular community addons
npm install storybook-dark-mode           # Dark mode toggle
npm install @storybook/addon-designs      # Figma embeds
npm install msw-storybook-addon           # API mocking with MSW
npm install storybook-addon-performance   # Performance profiling
npm install @storybook/addon-coverage      # Code coverage

// .storybook/main.ts
const config: StorybookConfig = {
  addons: [
    "@storybook/addon-essentials",
    "@storybook/addon-a11y",
    "@storybook/addon-interactions",
    "storybook-dark-mode",
    "@storybook/addon-designs",
  ],
};
// Using MSW addon to mock API calls in stories
import { http, HttpResponse } from "msw";

export const WithData: Story = {
  parameters: {
    msw: {
      handlers: [
        http.get("/api/users", () => {
          return HttpResponse.json([
            { id: 1, name: "Alice", role: "admin" },
            { id: 2, name: "Bob", role: "user" },
          ]);
        }),
      ],
    },
  },
};

export const WithError: Story = {
  parameters: {
    msw: {
      handlers: [
        http.get("/api/users", () => {
          return HttpResponse.json(
            { error: "Internal Server Error" },
            { status: 500 }
          );
        }),
      ],
    },
  },
};

TypeScript 集成

Storybook 8 具有一流的 TypeScript 支持。Meta 和 StoryObj 类型为故事提供完整的类型检查。TypeScript 属性类型自动提取以生成 ArgTypes 和 Controls。

TypeScript 的 satisfies 操作符与 CSF3 配合良好,提供类型检查同时允许 Storybook 推断每个故事的具体 args。

// Full TypeScript example with strict typing
import type { Meta, StoryObj } from "@storybook/react";
import { DataTable, type DataTableProps } from "./DataTable";

type User = {
  id: number;
  name: string;
  email: string;
  role: "admin" | "editor" | "viewer";
};

// satisfies provides type checking without widening
const meta = {
  title: "Data/DataTable",
  component: DataTable<User>,
  tags: ["autodocs"],
  argTypes: {
    sortDirection: {
      control: "radio",
      options: ["asc", "desc"] as const,
    },
    onRowClick: { action: "row-clicked" },
  },
} satisfies Meta<DataTableProps<User>>;

export default meta;
type Story = StoryObj<typeof meta>;

const sampleUsers: User[] = [
  { id: 1, name: "Alice", email: "alice@co.com", role: "admin" },
  { id: 2, name: "Bob", email: "bob@co.com", role: "editor" },
];

export const Default: Story = {
  args: {
    data: sampleUsers,
    columns: ["name", "email", "role"],
    sortable: true,
    sortDirection: "asc",
  },
};

CI/CD 集成

Storybook 通过 test-runner 包和 Chromatic 集成到 CI 流程。test-runner 在无头浏览器中执行所有 play 函数。

典型的 CI 流程:运行 test-runner 进行交互测试,发布到 Chromatic 进行视觉回归测试,部署构建后的 Storybook 供评审。

# Install the test runner
npm install --save-dev @storybook/test-runner

# Run interaction tests locally
npx test-storybook

# Run with coverage
npx test-storybook --coverage

# package.json scripts
"scripts": {
  "storybook": "storybook dev -p 6006",
  "build-storybook": "storybook build",
  "test-storybook": "test-storybook",
  "test-storybook:ci": "concurrently -k -s first \
    \"npx http-server storybook-static -p 6006\" \
    \"npx wait-on tcp:6006 && test-storybook\""
}

# GitHub Actions workflow
# .github/workflows/storybook.yml
name: Storybook Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npm run build-storybook
      - run: npm run test-storybook:ci
      - uses: chromaui/action@latest
        with:
          projectToken: \${{ secrets.CHROMATIC_TOKEN }}

性能优化与最佳实践

随着组件库增长,Storybook 构建和启动时间可能增加。以下最佳实践帮助保持工作流高效。

  • 使用 Vite 构建器(Storybook 8 默认)获得更快的启动和 HMR。
  • 启用懒编译,只编译导航到的故事,减少初始启动时间。
  • 每个组件一个故事文件,与组件文件放在一起便于发现。
  • 使用 args 而非硬编码属性,使每个变体可通过 Controls 探索。
  • 在尽可能低的层级应用装饰器,避免切换故事时不必要的重渲染。
  • 使用 play 函数进行交互测试,保持测试靠近故事。
  • 配置 autodocs 自动生成文档,而非手动维护 MDX 文件。
  • 使用装饰器和 MSW 模拟外部依赖,而非真实后端。
  • 使用组合功能在 monorepo 中合并多个 Storybook。
  • 设置 Chromatic TurboSnap 只捕获受代码变更影响的故事快照,减少 CI 时间和成本。
// Recommended project structure
src/
  components/
    Button/
      Button.tsx            # Component
      Button.stories.ts     # Stories (co-located)
      Button.test.ts        # Unit tests
      Button.mdx            # Custom docs (optional)
      Button.module.css     # Styles
      index.ts              # Barrel export
    Card/
      Card.tsx
      Card.stories.ts
      index.ts
  .storybook/
    main.ts                 # Build config
    preview.ts              # Runtime config
    manager.ts              # UI customization

常见问题

Storybook 用来做什么?

Storybook 用于在隔离环境中开发、测试和文档化 UI 组件。团队用它进行组件驱动开发、设计系统维护、视觉回归测试、无障碍审计和生成活文档。

什么是 Storybook 中的 CSF3?

CSF3 是 Storybook 8 中的最新故事格式。故事定义为带有 args 属性的普通对象,更简洁、可组合且类型安全。

Storybook 中的 play 函数如何工作?

Play 函数是附加到故事的异步函数,使用 Testing Library 模拟用户交互。故事加载时自动执行,test-runner 可在 CI 中执行。

Storybook 和 Chromatic 有什么区别?

Storybook 是开源组件工作台,Chromatic 是付费云服务,提供视觉回归测试和协作审查工作流。

Storybook 支持 Vue 和 Angular 吗?

支持。Storybook 8 支持 React、Vue 3、Angular、Svelte、Web Components 等。

如何在 Storybook 中测试无障碍性?

安装 a11y 插件,它集成 axe-core 自动运行无障碍审计并在面板中显示违规。

可以从 Storybook 生成文档吗?

可以。Storybook 8 的 autodocs 自动从故事和类型生成文档页面,也支持自定义 MDX 文档。

如何将 Storybook 集成到 CI/CD?

使用 test-runner 在 CI 中执行 play 函数,集成 Chromatic 进行视觉回归测试,部署静态 Storybook 供评审。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON Formatter🌈CSS Gradient Generator🎨Color Converter

相关文章

React 设计模式指南:复合组件、自定义 Hook、HOC、Render Props 与状态机

完整的 React 设计模式指南,涵盖复合组件、render props、自定义 hooks、高阶组件、Provider 模式、状态机、受控与非受控、组合模式、观察者模式、错误边界和模块模式。

Vitest 完全指南:现代 JavaScript/TypeScript 快速单元测试 (2026)

完整的 Vitest 指南,涵盖安装配置、测试语法、Mock、快照测试、代码覆盖率、Vue/React 组件测试、TypeScript 集成、Vitest UI 和从 Jest 迁移。

Playwright E2E 测试完全指南

学习 Playwright 端到端测试。