[Web翻译]介绍Storybook新的Args参数体系

1,551 阅读7分钟

副标题:下一代动态组件示例

原文地址:medium.com/storybookjs…

原文作者:medium.com/@shilman

发布时间:2020年7月9日 - 7分钟阅读

Storybook是世界上最流行的UI组件工场,它被Airbnb、Slack、Lyft、IBM、Shopify以及数千名行业顶尖团队使用。它被Airbnb、Slack、Lyft、IBM、Shopify以及整个行业的数千个顶级团队所使用。

在其核心,Storybook是一个故事目录:小型Javascript函数,可以捕获孤立的UI组件状态,使它们对交互式开发、测试和文档非常有用。

现在在Storybook 6.0中,我们很高兴地引入了Storybook Args,这是编写故事的一项基础性改进。Args允许故事接收动态数据作为输入参数,为故事的人机工程学可移植性重复使用打开了一个新的水平。

Args与您现有的故事兼容,但却释放了新的功能。

  • 🗜 减少故事的大小和复杂性
  • 🚚跨故事重用夹具数据
  • ♻️在更多的工具中回收故事

它们也为强大的下一代Storybook插件打开了大门,我们将在一系列后续文章中介绍。


💡为什么是动态数据?

Storybook的组件故事格式(CSF)是一个新兴的组件示例标准,在Storybook 5.2中引入。CSF文件是ES6模块,没有任何Storybook特有的扩展。因此,它们是简单的、可移植的和面向未来的。

export const Basic = () => (
  <Button label='hello' />
);

编写一个CSF故事很容易。你在你想显示的状态中实例化你的组件,将其封装在一个函数中,并将其命名为CSF故事。

但它还可以改进。Tom Coleman和我在研究了45,000多个Github仓库中CSF的使用情况后,有了一个深刻的认识:如果故事接受动态数据作为输入,它将解锁无数新的用例。

考虑一下我们之前例子的以下迭代。

export const Basic = (args) => <Button {...args} />;
Basic.args = { label: 'hello' };

这个新版本从 "环境 "中接收一个Args对象,并在这个对象中声明它想要接收的初始数据。

这种间接的技巧看起来是一件小事,但它是一个强大的抽象,是下一代Storybook功能的基础。在本篇文章中,我们将重点介绍Args使编写故事更加高效的不同方式。

动态数据使您能够实时编辑组件

🗜减少故事的模板

Args通过在故事的数据和显示逻辑之间提供更清晰的分离,减少了用户需要为每个故事编写和维护的代码。

考虑以下CSF v1的故事。

// Button.stories.js
export const Text = () => (
  <Button label="hello" background="#ff0" />
);
export const Emoji = () => (
  <Button label="😀 😎 👍 💯" background="#ff0" />
);

现在考虑用Args.stories.js来做同样的故事。

// 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: '😀 😎 👍 💯' };

故事函数模板在各个故事中重复使用。Template.bind({})会复制一个函数,减少代码重复

同样的,...Text.args也会制作一份数据的副本,减少数据的重复

当为其他框架编写故事时,其好处变得更加明显。考虑一下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故事的模式与早期的CSF版本相比有所改变,但我们认为你会喜欢它。如果你喜欢用 "老 "的方式写故事,或者逐步采用,Storybook完全向后兼容。

🚚跨故事重用固定数据

Args的第二个主要用户好处是,它为你的组件提供了一种组织和共享夹具数据的风格化方式。

你可能熟悉编程中的DRY原则。不要重复自己。但在实践中,我们会看到很多重复的故事。

考虑一个假设的任务列表的复合组件。

// 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故事来反映新的模式。因为你的Args故事重用了固定数据,模式的变化将在故事层次结构中传播,从而大大降低了维护成本。

当为大型应用程序构建故事书时,这种数据重用特别有用,因为在大型应用程序中,组件是分层组织的。

Args的大部分设计灵感来自Chromatic的故事书,它包含了成千上万的故事,从原子组件一直到完整的应用页面。

Args将这些学习到的知识编码到故事语言本身,将多年开发和使用Storybook的最佳实践烘焙成顶级从业者的经验。

♻️在其他工具中回收故事

最后一个好处是,Args故事比以前更容易移植。

组件故事格式的一个设计目标是创建高度可移植的组件示例。在我们的公告文章中,我们展示了CSF故事如何在Jest单元测试中循环使用,并描绘了前端工具空间中更大的互操作性图景。

随着CSF在整个生态系统中的采用,这幅图景现在正在成为现实。它内置在热门的新React框架RedwoodJS中。它为强大的在线IDE webcomponents.dev提供了动力。而且在写这篇文章的时候,至少有一个流行的设计工具正在采用它。

Args通过从你的故事中移除附加组件的依赖性,使互操作性更进一步。考虑以下CSF v1的故事,它使用了动作和旋钮,这两个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' }

请注意,这个故事不再依赖于@storybook/*包。Args是 "由环境 "提供的,所以即使没有直接的故事依赖,我们也可以保留动作和旋钮式的功能。我们将在后续的文章中介绍自动生成的控件和动作时解释更多的工作原理。

去除这些Storybook依赖性增加了可移植性,因此故事变得更容易在外部设计、原型设计、开发和测试工具中使用。

考虑如何在Jest中回收上面的Args故事。

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故事编写同样的测试需要复杂的嘲讽。现在不需要了! 而且我们很高兴能在整个前端生态系统中扩散基于Args的组件示例。

⚡1分钟安装

在过去几个月使用args之后,我们认为所有的用户都应该在今天升级。今天就在Storybook 6.0中试试吧。 升级一个现有的Storybook项目。

npx sb upgrade

或者将Storybook引导到一个现有的应用程序中。

npx sb init

要创建你的第一个Args故事,请创建一个函数,将一个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故事。你已经写出了你的第一个Args故事。但这仅仅是冰山一角。Args还为Storybook增加了附加组件,并带来了许多新的功能,我们将在未来几周内详细介绍。

请在下面订阅,了解Args是如何实现的。

  • 🎛 自动生成组件属性的控件
  • 📢自动生成事件记录的动作
  • 🛠 为主题、国际化和上下文定制工具栏

参与进来

Args由Michael Shilman(我!)和Tom Coleman开发,灵感来自addon-docs/knobs/context的贡献者,包括Norbert de LangenFilipp RiabchunAtanas StoyanovLeo Y. Li

Storybook由1000多名开源贡献者维护,并由顶级维护者组成的指导委员会指导。如果你对贡献感兴趣,可以在GitHub上查看Storybook,创建一个问题,或者提交一个拉取请求。在Open Collective上捐款。在Discord上与我们聊天--通常会有维护者在线。

通过Twitter和注册我们的官方邮件列表来了解Storybook的最新动态:

upscri.be/d42fc0?as_e…

感谢Tom Coleman。


通过www.DeepL.com/Translator (免费版)翻译