Storybook 基础使用 #
界面概览 #
Storybook 启动后,你将看到以下界面布局:
text
┌─────────────────────────────────────────────────────────────────────────┐
│ Storybook 🔍 搜索 ⚙️ 设置 │
├────────────────┬────────────────────────────────────────────────────────┤
│ │ │
│ 侧边栏 │ Canvas 画布 │
│ (Sidebar) │ │
│ │ ┌─────────────────────────┐ │
│ 📁 Components │ │ │ │
│ 📄 Button │ │ [组件预览区域] │ │
│ Primary │ │ │ │
│ Secondary │ │ Primary Button │ │
│ Disabled │ │ │ │
│ 📄 Input │ └─────────────────────────┘ │
│ Default │ │
│ WithIcon │ │
│ │ │
│ 📁 Pages │ │
│ 📄 Home │ │
│ │ │
├────────────────┴────────────────────────────────────────────────────────┤
│ Addons 面板 (Controls | Actions | Interactions | ...) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ variant: [primary ▼] size: [medium ▼] disabled: [ ] │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
侧边栏(Sidebar) #
导航结构 #
侧边栏按照层级组织 Stories:
text
📁 Components ← 目录(由 title 生成)
📄 Button ← 组件(Story 文件)
Primary ← Story(具体状态)
Secondary
Disabled
📄 Input
Default
WithIcon
Error
📁 Pages
📄 Home
LoggedIn
LoggedOut
搜索功能 #
使用搜索框快速定位组件:
text
┌─────────────────────────────────────┐
│ 🔍 搜索组件或 Story... │
└─────────────────────────────────────┘
支持:
- 组件名称搜索
- Story 名称搜索
- 模糊匹配
- 快捷键:/ 或 Ctrl/Cmd + K
折叠与展开 #
text
点击目录或组件可以折叠/展开:
▶ 📁 Components ← 折叠状态
▼ 📁 Components ← 展开状态
📄 Button
Primary
Secondary
Canvas 画布 #
组件预览区域 #
Canvas 是组件的主要展示区域:
text
┌─────────────────────────────────────────────────────────────┐
│ Canvas │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Button │ ← 组件渲染区域 │
│ └─────────────┘ │
│ │
│ 工具栏: │
│ 🔲 📐 🎨 📱 🔗 │
│ │ │ │ │ │ │
│ │ │ │ │ └─ 在新窗口打开 │
│ │ │ │ └───── 视口切换 │
│ │ │ └───────── 背景切换 │
│ │ └───────────── 测量/轮廓 │
│ └───────────────── 全屏 │
│ │
└─────────────────────────────────────────────────────────────┘
工具栏功能 #
| 图标 | 功能 | 说明 |
|---|---|---|
| 🔲 | 全屏 | 切换全屏模式 |
| 📐 | 测量 | 显示组件尺寸信息 |
| 🎨 | 背景 | 切换背景颜色 |
| 📱 | 视口 | 切换设备视口 |
| 🔗 | 新窗口 | 在新窗口打开 Story |
视口切换 #
text
┌─────────────────────────────────────┐
│ 📱 视口选择 │
├─────────────────────────────────────┤
│ │
│ ┌─────────┐ │
│ │ Mobile │ 375 x 812 │
│ └─────────┘ │
│ ┌─────────┐ │
│ │ Tablet │ 768 x 1024 │
│ └─────────┘ │
│ ┌─────────┐ │
│ │ Desktop │ 1280 x 800 │
│ └─────────┘ │
│ │
└─────────────────────────────────────┘
Docs 文档页面 #
文档视图 #
点击 Docs 标签查看组件文档:
text
┌─────────────────────────────────────────────────────────────┐
│ Canvas | Docs │
├─────────────────────────────────────────────────────────────┤
│ │
│ Button │
│ ========== │
│ │
│ 按钮组件用于触发操作。 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Controls │ │
│ │ variant: [primary ▼] │ │
│ │ size: [medium ▼] │ │
│ │ disabled: [ ] │ │
│ │ children: "Button" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Stories │
│ ------- │
│ │
│ Primary │
│ ┌─────────────┐ │
│ │ Button │ │
│ └─────────────┘ │
│ │
│ Secondary │
│ ┌─────────────┐ │
│ │ Button │ │
│ └─────────────┘ │
│ │
│ Args Table │
│ ---------- │
│ | Name | Description | Type | Default | │
│ |------|-------------|------|---------| │
│ | variant | 按钮类型 | string | 'primary' | │
│ | size | 按钮尺寸 | string | 'medium' | │
│ │
└─────────────────────────────────────────────────────────────┘
自动文档 #
Storybook 自动从组件生成文档:
javascript
// Button.jsx
import React from 'react';
import PropTypes from 'prop-types';
/**
* 按钮组件用于触发操作
* @param {Object} props - 组件属性
* @param {'primary'|'secondary'} props.variant - 按钮变体类型
* @param {'small'|'medium'|'large'} props.size - 按钮尺寸
* @param {boolean} props.disabled - 是否禁用
*/
const Button = ({ variant, size, disabled, children, onClick }) => {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
};
Button.propTypes = {
variant: PropTypes.oneOf(['primary', 'secondary']),
size: PropTypes.oneOf(['small', 'medium', 'large']),
disabled: PropTypes.bool,
children: PropTypes.node.isRequired,
onClick: PropTypes.func,
};
Button.defaultProps = {
variant: 'primary',
size: 'medium',
disabled: false,
};
export default Button;
Controls 控件面板 #
控件类型 #
Controls 面板允许动态修改组件参数:
text
┌─────────────────────────────────────────────────────────────┐
│ Controls │
├─────────────────────────────────────────────────────────────┤
│ │
│ variant │
│ ┌─────────────────────────────────────────┐ │
│ │ primary ▼ │ ← select │
│ └─────────────────────────────────────────┘ │
│ │
│ size │
│ ○ small ● medium ○ large ← radio │
│ │
│ disabled │
│ ☐ disabled ← boolean │
│ │
│ children │
│ ┌─────────────────────────────────────────┐ │
│ │ Button │ ← text │
│ └─────────────────────────────────────────┘ │
│ │
│ count │
│ ┌──┬───────────────────────────────────┬──┐ │
│ │ -│███████████████████████████████████│+ │ ← range │
│ └──┴───────────────────────────────────┴──┘ │
│ 5 │
│ │
│ color │
│ ┌─────────────────────────────────────────┐ │
│ │ #1890ff │ ← color │
│ └─────────────────────────────────────────┘ │
│ │
│ onClick │
│ ┌─────────────────────────────────────────┐ │
│ │ () => alert('clicked') │ ← object │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
控件配置 #
javascript
// Button.stories.js
export default {
title: 'Components/Button',
component: Button,
argTypes: {
// 下拉选择
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
description: '按钮变体类型',
table: {
defaultValue: { summary: 'primary' },
},
},
// 单选按钮
size: {
control: 'radio',
options: ['small', 'medium', 'large'],
},
// 开关
disabled: {
control: 'boolean',
},
// 文本输入
children: {
control: 'text',
},
// 数字范围
count: {
control: { type: 'range', min: 0, max: 10, step: 1 },
},
// 颜色选择
color: {
control: 'color',
},
// 对象编辑
style: {
control: 'object',
},
// 隐藏控件
className: {
control: false,
},
},
};
控件类型一览 #
| 控件类型 | 适用数据类型 | 示例 |
|---|---|---|
| text | string | 文本输入框 |
| boolean | boolean | 开关 |
| number | number | 数字输入 |
| range | number | 滑块 |
| color | string | 颜色选择器 |
| select | string/number | 下拉选择 |
| radio | string/number | 单选按钮 |
| inline-radio | string/number | 行内单选 |
| check | string[] | 复选框 |
| inline-check | string[] | 行内复选 |
| object | object | JSON 编辑器 |
| file | string | 文件选择 |
| date | Date | 日期选择 |
Actions 操作面板 #
事件日志 #
Actions 面板记录组件触发的事件:
text
┌─────────────────────────────────────────────────────────────┐
│ Actions │
├─────────────────────────────────────────────────────────────┤
│ │
│ 📋 onClick │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "type": "click", │ │
│ │ "target": "button", │ │
│ │ "timestamp": 1640000000000 │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 📋 onChange │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ "new value" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 清除日志 │
│ │
└─────────────────────────────────────────────────────────────┘
配置 Actions #
javascript
// .storybook/preview.js
const preview = {
parameters: {
actions: {
// 自动捕获以 on 开头的事件处理函数
argTypesRegex: '^on[A-Z].*',
},
},
};
// 或在 Story 中手动配置
export const WithClick = {
args: {
onClick: action('button-clicked'),
onChange: action('value-changed'),
},
};
Interactions 交互面板 #
交互测试 #
Interactions 面板显示交互测试步骤:
text
┌─────────────────────────────────────────────────────────────┐
│ Interactions │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✅ Play function │
│ │
│ ✅ 1. Find button │
│ await screen.findByRole('button') │
│ │
│ ✅ 2. Click button │
│ await userEvent.click(button) │
│ │
│ ✅ 3. Verify click handler │
│ expect(onClick).toHaveBeenCalled() │
│ │
│ ▶️ 重新运行 │
│ │
└─────────────────────────────────────────────────────────────┘
快捷键 #
常用快捷键 #
| 快捷键 | 功能 |
|---|---|
/ 或 Ctrl/Cmd + K |
搜索 |
S |
聚焦侧边栏 |
D |
切换 Canvas/Docs |
A |
切换 Addons 面板 |
F |
全屏 |
← → |
上一个/下一个 Story |
↑ ↓ |
导航 Stories 列表 |
Enter |
选择 Story |
Escape |
关闭搜索/退出全屏 |
第一个 Story #
创建组件 #
jsx
// src/components/Button.jsx
import React from 'react';
import './Button.css';
const Button = ({
variant = 'primary',
size = 'medium',
disabled = false,
children,
onClick,
}) => {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
};
export default Button;
创建 Story #
javascript
// src/components/Button.stories.jsx
import Button from './Button';
export default {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
},
size: {
control: 'radio',
options: ['small', 'medium', 'large'],
},
},
};
export const Primary = {
args: {
variant: 'primary',
children: 'Primary Button',
},
};
export const Secondary = {
args: {
variant: 'secondary',
children: 'Secondary Button',
},
};
export const Large = {
args: {
size: 'large',
children: 'Large Button',
},
};
export const Disabled = {
args: {
disabled: true,
children: 'Disabled Button',
},
};
查看结果 #
启动 Storybook 后:
bash
npm run storybook
你将看到:
text
📁 Components
📄 Button
Primary ← 主要按钮
Secondary ← 次要按钮
Large ← 大尺寸按钮
Disabled ← 禁用按钮
组织 Stories #
目录结构 #
text
┌─────────────────────────────────────────────────────────────┐
│ Story 组织方式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 方式一:按类型分组 │
│ │
│ title: 'Components/Button' │
│ title: 'Components/Input' │
│ title: 'Layouts/Grid' │
│ title: 'Pages/Home' │
│ │
│ 方式二:按功能分组 │
│ │
│ title: 'UI/Buttons/Primary' │
│ title: 'UI/Forms/Input' │
│ title: 'UI/Forms/Select' │
│ │
│ 方式三:按模块分组 │
│ │
│ title: 'Auth/LoginForm' │
│ title: 'Auth/RegisterForm' │
│ title: 'Dashboard/Stats' │
│ │
└─────────────────────────────────────────────────────────────┘
使用斜杠分隔 #
javascript
// 使用 / 分隔层级
export default {
title: 'Design System/Atoms/Button',
component: Button,
};
// 结果:
// 📁 Design System
// 📁 Atoms
// 📄 Button
// Primary
// Secondary
最佳实践 #
1. 命名规范 #
javascript
// ✅ 好的命名
export const PrimaryButton = {};
export const WithIcon = {};
export const LoadingState = {};
export const ErrorState = {};
// ❌ 不好的命名
export const Story1 = {};
export const Test = {};
export const Default = {}; // 除非真的是默认状态
2. Story 组织 #
javascript
export default {
title: 'Components/Button',
component: Button,
// 按逻辑分组
subcomponents: { ButtonGroup },
};
// 基础状态
export const Primary = {};
export const Secondary = {};
// 尺寸变体
export const Small = {};
export const Medium = {};
export const Large = {};
// 状态变体
export const Loading = {};
export const Disabled = {};
// 组合示例
export const WithIcon = {};
export const WithBadge = {};
3. 文档描述 #
javascript
export default {
title: 'Components/Button',
component: Button,
parameters: {
docs: {
description: {
component: `
按钮组件用于触发操作。
## 使用指南
- 使用 \`primary\` 变体作为主要操作
- 使用 \`secondary\` 变体作为次要操作
- 禁用状态用于不可用操作
`,
},
},
},
};
下一步 #
现在你已经掌握了 Storybook 的基础使用方法,接下来学习 编写 Stories 深入了解 Story 的编写技巧!
最后更新:2026-03-29