
Storybook Args 简介
下一代,动态组件示例

Storybook 是世界上最受欢迎的 UI 组件工作坊。它被 Airbnb、Slack、Lyft、IBM、Shopify 以及行业内数千个顶级团队使用。
Storybook 的核心是一个 stories 目录:小的 Javascript 函数,用于捕获隔离的 UI 组件状态,使其在交互式开发、测试和文档方面非常有用。
现在在 Storybook 6.0 中,我们很高兴推出 Storybook Args,这是编写 stories 的一项基础性改进。Args 允许 stories 接收动态数据作为输入参数,从而解锁故事 人体工程学、可移植性 和重用性 的新水平。
Args 与您现有的 stories 兼容,但解锁了新功能
- 🗜 减少 stories 的大小和复杂性
- 🚚 在 stories 之间重用 fixture 数据
- ♻️ 在更广泛的工具中循环使用 stories
它们还为强大的下一代 Storybook 插件打开了大门,我们将在后续系列文章中介绍这些插件。
💡 为什么需要动态数据?
Storybook 的 组件故事格式 (CSF) 是组件示例的新兴标准,在 Storybook 5.2 中引入。CSF 文件是 ES6 模块,没有任何 Storybook 特定的扩展。因此,它们简单、可移植且面向未来
export const Basic = () => (
<Button label='hello' />
);
编写 CSF story 很简单。您可以在想要显示的状态下实例化组件,将其包装在一个函数中,并命名它。
但这可以改进。Tom Coleman 和我在研究了 CSF 在 45,000 多个 Github 仓库中的使用情况后,有了一个见解:如果 stories 接受动态数据作为输入,它将解锁无数新的用例。
考虑以下我们之前示例的迭代
export const Basic = (args) => <Button {...args} />;
Basic.args = { label: 'hello' };
这个新版本从“环境”接收一个 Args 对象,并声明它想要在该对象中接收的初始数据。
这种间接的技巧可能看起来是件小事,但它是一个强大的抽象,它支撑着下一代 Storybook 功能。在这篇文章中,我们将重点介绍 Args 使编写 stories 更高效的不同方式。

🗜 减少 story 样板代码
Args 通过在 story 的数据和显示逻辑之间提供更清晰的分离,减少了用户需要为每个 story 编写和维护的代码。
考虑以下 CSF v1 stories
// Button.stories.js
export const Text = () => (
<Button label="hello" background="#ff0" />
);
export const Emoji = () => (
<Button label="😀 😎 👍 💯" background="#ff0" />
);
现在考虑使用 Args 的相同 stories
// Button.stories.js
const Template = (args) => <Button {...args} />;
export const Text = Template.bind({});
Text.args = { label: 'hello', background: '#ff0' };
export const Emoji = Template.bind({});
Emoji.args = { ...Text.args, label: '😀 😎 👍 💯' };
story 函数样板代码在 stories 之间重用:Template.bind({})
复制该函数,减少代码重复。
同样,...Text.args
复制数据,减少数据重复。
当为其他框架编写 stories 时,优势变得更加明显。考虑 Vue 的等效示例
// Button.stories.js
const Template = (args) => ({
props: Object.keys(args),
components: { MyButton },
template: `
<my-button :background="background">
{{label}}
</my-button>
`
});
export const Text = Template.bind({});
Text.args = { label: 'hello', background: '#ff0' };
export const Emoji = Template.bind({});
Emoji.args = { ...Text.args, label: '😀 😎 👍 💯' };
Args stories 的这种模式与早期的 CSF 版本有所不同,但我们认为您会喜欢它。如果您喜欢以“旧”方式编写 stories,或者逐步采用,Storybook 完全向后兼容。
🚚 在 stories 之间重用 fixture 数据
Args 的第二个主要用户好处是,它提供了一种风格化的方式来组织和共享组件的 fixture 数据。
您可能熟悉编程中的 DRY 原则:不要重复自己。但在实践中,我们在 stories 中看到很多重复。
考虑一个假想任务列表的复合组件
// TaskList.stories.js
const owner = { login: 'shilman', avatar: ... };
export const Basic = () => (
<TaskList items={[
{ title: 'checked item', checked: true, owner },
{ title: 'checked item', checked: true, owner },
{ title: 'unchecked item', checked: false, owner },
{ title: 'unassigned item', checked: false, },
]}/>
);
现在考虑 Args 等效项
// TaskList.stories.js
import { Checked, Unchecked, Unassigned } from '../Task.stories';
const Template = (args) => <TaskList {...arg} />;
export const Basic = Template.bind({});
Basic.args = {
items: [Checked.args, Checked.args, Unchecked.args, Unassigned.args]
};
在 Args 示例中,当 Task
组件的签名更改时,您只需要更改 Tasks stories 以反映新的 schema。因为您的 Args stories 重用 fixture 数据,所以 schema 更改将向上层 story 层次结构传播,从而大大降低维护成本。
这种数据重用在为大型应用程序构建 Storybooks 时特别有用,在这些应用程序中,组件是分层组织的。
Args 的许多设计灵感来自 Chromatic 的 storybook,其中包含数千个 stories,范围从 原子 组件一直到完整的应用程序页面。
Args 将这些学习经验编码到 story 语言本身中,融入了多年来作为顶级从业者开发和使用 Storybook 所获得的宝贵最佳实践。
♻️ 在其他工具中循环使用 stories
最后一个好处是 Args stories 比以前更具可移植性。
组件故事格式的一个设计目标是创建高度可移植的组件示例。在我们的 公告帖子中,我们展示了如何在 Jest 单元测试中循环使用 CSF story,并描绘了前端工具空间中互操作性的更大前景。
随着 CSF 在整个生态系统中的采用,这一愿景现在正在成为现实。它内置于热门的新 React 框架 RedwoodJS 中。它为强大的在线 IDE webcomponents.dev 提供支持。并且截至撰写本文时,它已被纳入至少一种流行的设计工具中。
Args 通过从您的 stories 中删除插件依赖项,使互操作性更进一步。考虑以下 CSF v1 story,它使用了 actions 和 knobs,这是 Storybook 最受欢迎的两个插件
import { action } from '@storybook/addon-actions';
import { text } from '@storybook/addon-knobs';
export default { title: 'Button' };
export const Text = () => (
<Button
onClick={action('onClick')}
label={text('label', 'Hello')}
/>
);
这个 ES6 模块是可移植的,但它依赖于 @storybook/addon-actions
和 @storybook/addon-knobs
,这两者都严重依赖 Storybook 的运行时。
现在考虑 Args 等效项
const Template = (args) => <Button {...args} />;
export const Text = Template.bind({});
Text.args = { label: 'Hello' }
请注意,这个 story 不再依赖于 @storybook/*
包。Args 由“环境”提供,因此即使没有直接的 story 依赖项,我们也可以保留 actions 和 knobs 风格的功能!当我们将在后续文章中介绍自动生成的 controls 和 actions 时,我们将更详细地解释这是如何工作的。
删除这些 Storybook 依赖项提高了可移植性,因此 story 在外部设计、原型设计、开发和测试工具中更容易使用。
考虑如何将上面的 Args story 在 Jest 中循环使用
import { render, fireEvent } from '@testing-library/react';
import { Text } from './Button.stories';
it('should respond to click events', () => {
const handleClick = jest.fn();
const instance = render(
<Text {...Text.args} onClick={handleClick} />
);
fireEvent.click(instance.container.firstChild);
expect(handleClick).toHaveBeenCalled();
});
为原始 CSF story 使用 knobs 和 actions 编写相同的测试将需要复杂的模拟。不再需要了!我们很高兴在整个前端生态系统中推广基于 Args 的组件示例。
⚡ 1 分钟安装
在过去几个月使用 args 后,我们认为所有用户都应该立即升级。立即在 Storybook 6.0 中试用!
升级现有的 Storybook 项目
npx sb upgrade
或将 Storybook 引导到现有应用程序中
npx sb init
要创建您的第一个 Args story,请创建一个函数,该函数将 Args 对象作为其第一个输入,然后使用您想要接收的数据注释该函数
import { Button } from '@storybook/react/demo';
export default { title: 'Button/Args', component: Button };
export const Basic = (args) => <Button {...args} />;
Basic.args = { children: 'hello' };
瞧!您已经编写了您的第一个 Args story。但这只是冰山一角。Args 还为插件增压,并为 Storybook 带来了许多新功能,我们将在未来几周内对此进行扩展。
在下面订阅以了解 Args 如何启用
- 🎛 组件属性的自动生成 控件,
- 📢 用于事件日志记录的自动生成 actions,
- 🛠 用于主题、国际化和上下文的自定义工具栏。
参与进来
Args 由 Michael Shilman (我!) 和 Tom Coleman 开发,灵感来自 addon-docs
/knobs
/context
贡献者,包括 Norbert de Langen、Filipp Riabchun、Atanas Stoyanov 和 Leo Y. Li。
Storybook 由 1000 多名开源贡献者维护,并由顶级维护者的指导委员会指导。如果您有兴趣贡献,请在 GitHub 上查看 Storybook,创建一个 issue,或提交一个 pull request。在 Open Collective 上捐款。在 Discord 中与我们聊天 — 维护者通常在线。