JavaScript内核与高级编程之:`JavaScript`的`Storybook`:其在组件开发和文档生成中的应用。

各位听众,大家好!今天咱们来聊聊一个前端开发神器——Storybook,这玩意儿能帮你把组件玩出花来,还能自动生成文档,简直是懒人福音,效率神器!

一、啥是Storybook?(别跟我说storybook是童话故事书!)

Storybook,可不是童话故事书,它是一个开源的 UI 组件开发工具。简单来说,它提供了一个隔离的环境,让你可以在不依赖整个应用的情况下,独立地开发、测试和展示你的 UI 组件。你可以把它想象成一个组件的“展览馆”,每个组件都有自己的“展位”,你可以随意调整灯光(props)、背景(theme),看看它们在不同场景下的表现。

二、为啥要用Storybook?(用了你就回不去了!)

用Storybook的好处那可太多了,就像用了飘柔,头发都顺滑了:

  1. 组件独立开发: 告别了“牵一发而动全身”的噩梦。不用启动整个应用,就可以专注地开发和调试单个组件。
  2. 组件文档自动化: 自动生成组件文档,再也不用手动维护那份永远滞后的文档了。
  3. 组件复用性提升: 清晰地展示了组件的各种状态和用法,方便团队成员理解和复用。
  4. 视觉测试: 方便进行视觉回归测试,确保组件在不同版本下的外观一致。
  5. 团队协作: 统一的组件库,方便团队成员共享和使用,避免重复造轮子。

三、Storybook怎么玩?(手把手教你!)

咱们以一个简单的React组件为例,来演示一下Storybook的用法。

3.1 安装Storybook:

首先,在一个现有的 React 项目中,或者新建一个项目,运行以下命令安装 Storybook:

npx storybook init

这个命令会自动检测你的项目类型,并安装相应的依赖。安装完成后,会生成一个 .storybook 目录,以及一些示例 stories。

3.2 创建你的第一个 Story:

假设我们有一个简单的按钮组件 Button.jsx

// Button.jsx
import React from 'react';
import PropTypes from 'prop-types';

const Button = ({ primary, backgroundColor, size, label, onClick }) => {
  const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
  return (
    <button
      type="button"
      className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
      style={backgroundColor ? { backgroundColor } : {}}
      onClick={onClick}
    >
      {label}
    </button>
  );
};

Button.propTypes = {
  primary: PropTypes.bool,
  backgroundColor: PropTypes.string,
  size: PropTypes.oneOf(['small', 'medium', 'large']),
  label: PropTypes.string.isRequired,
  onClick: PropTypes.func,
};

Button.defaultProps = {
  backgroundColor: null,
  primary: false,
  size: 'medium',
  onClick: undefined,
};

export default Button;

然后,我们创建一个对应的 Story 文件 Button.stories.jsx

// Button.stories.jsx
import React from 'react';
import Button from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
    onClick: { action: 'clicked' },
  },
};

const Template = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Primary Button',
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: 'Secondary Button',
};

export const Large = Template.bind({});
Large.args = {
  size: 'large',
  label: 'Large Button',
};

export const Small = Template.bind({});
Small.args = {
  size: 'small',
  label: 'Small Button',
};

代码解释:

  • title: 定义了 Story 在 Storybook 导航栏中的路径。
  • component: 指定了 Story 对应的组件。
  • argTypes: 定义了组件的 props 以及它们的控制方式。 control: 'color' 表示 backgroundColor 可以在 Storybook 中通过颜色选择器进行调整。 onClick: { action: 'clicked' } 表示点击事件会被 Storybook 记录。
  • Template: 一个函数,用于渲染组件。
  • Primary, Secondary, Large, Small: 不同的 Story,分别展示了按钮在不同状态下的表现。 args 对象定义了每个 Story 的 props 值。

3.3 运行Storybook:

在项目根目录下运行以下命令启动 Storybook:

npm run storybook

或者

yarn storybook

Storybook 会在浏览器中打开,你可以看到你的按钮组件,以及各种 Story。

四、Storybook进阶玩法:(让你的组件更上一层楼!)

4.1 使用 Addons:

Storybook 的强大之处在于它的 Addons。 Addons 可以扩展 Storybook 的功能,比如:

  • @storybook/addon-knobs 允许你在 Storybook 中动态修改组件的 props。 (已经过时,推荐使用@storybook/addon-essentials自带的 Controls)
  • @storybook/addon-viewport 模拟不同的设备屏幕尺寸。
  • @storybook/addon-a11y 检查组件的可访问性。
  • @storybook/addon-interactions: 用于测试组件的交互行为。

安装 Addons:

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

配置 Addons:

.storybook/main.js 文件中配置 Addons:

// .storybook/main.js
module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-viewport',
    '@storybook/addon-a11y',
    '@storybook/addon-interactions',
    '@storybook/addon-links',
  ],
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-webpack5',
  },
};

4.2 使用 Controls (替代 Knobs):

Controls@storybook/addon-essentials 自带的功能,可以让你在 Storybook 中动态修改组件的 props,比 Knobs 更强大,更易用。

在你的 Story 文件中,通过 argTypes 来定义组件的 props 以及它们的控制方式:

// Button.stories.jsx
import React from 'react';
import Button from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
    label: { control: 'text' },
    size: { control: { type: 'select', options: ['small', 'medium', 'large'] } },
    onClick: { action: 'clicked' },
  },
};

const Template = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Primary Button',
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: 'Secondary Button',
};

export const Large = Template.bind({});
Large.args = {
  size: 'large',
  label: 'Large Button',
};

export const Small = Template.bind({});
Small.args = {
  size: 'small',
  label: 'Small Button',
};

代码解释:

  • control: 'color': 使用颜色选择器控制 backgroundColor。
  • control: 'text': 使用文本输入框控制 label。
  • control: { type: 'select', options: ['small', 'medium', 'large'] }: 使用下拉选择框控制 size,并指定可选的值。
  • control: 'boolean': 使用开关控制 boolean 类型的 prop。
  • control: { type: 'number', min: 0, max: 100, step: 1 }: 使用数字输入框控制 number 类型的 prop,并指定最小值、最大值和步长。

4.3 使用 Interactions Addon进行交互测试:

@storybook/addon-interactions可以让你在 Storybook 中模拟用户交互,并对组件进行交互测试。

首先,安装这个addon:

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

然后在你的story文件中,使用play函数来模拟用户交互和进行断言。

// Button.stories.jsx
import React from 'react';
import Button from './Button';
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
    label: { control: 'text' },
    size: { control: { type: 'select', options: ['small', 'medium', 'large'] } },
    onClick: { action: 'clicked' },
  },
};

const Template = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Primary Button',
};

Primary.play = async ({ canvasElement }) => {
  const canvas = within(canvasElement);
  const button = await canvas.getByRole('button', { name: /Primary Button/i });
  await userEvent.click(button);
  expect(button).toBeVisible(); // 简单的断言,确保按钮是可见的
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: 'Secondary Button',
};

export const Large = Template.bind({});
Large.args = {
  size: 'large',
  label: 'Large Button',
};

export const Small = Template.bind({});
Small.args = {
  size: 'small',
  label: 'Small Button',
};

在这个例子中,Primary.play 函数模拟了用户点击 Primary Button 的操作,并使用 expect 断言按钮是可见的。 当你在 Storybook 中查看 Primary 这个 story 时, Storybook 会自动执行 play 函数,并显示测试结果。

4.4 使用 DocsPage:

Storybook 默认提供 DocsPage addon, 可以根据你的组件和 story 自动生成文档。 你需要在.storybook/main.js中确保@storybook/addon-docs在addons列表中. 并且,你可以在 story 文件中添加组件的描述和使用示例。

// Button.stories.jsx
import React from 'react';
import Button from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
    label: { control: 'text' },
    size: { control: { type: 'select', options: ['small', 'medium', 'large'] } },
    onClick: { action: 'clicked' },
  },
  parameters: {
    docs: {
      description: {
        component: 'A simple button component.',
      },
    },
  },
};

const Template = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Primary Button',
};

Primary.parameters = {
  docs: {
    description: {
      story: 'The primary button style.',
    },
  },
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: 'Secondary Button',
};

export const Large = Template.bind({});
Large.args = {
  size: 'large',
  label: 'Large Button',
};

export const Small = Template.bind({});
Small.args = {
  size: 'small',
  label: 'Small Button',
};

在上面的例子中,我们使用 parameters.docs.description.component 添加了组件的描述,使用 parameters.docs.description.story 添加了每个 story 的描述。 这些描述会显示在 Storybook 的 Docs 页面中。

五、Storybook的架构和原理(了解底层才能玩得更溜!)

Storybook 的核心架构包括以下几个部分:

  • Core: Storybook 的核心引擎,负责加载、解析和渲染 stories。
  • Addons API: 允许开发者扩展 Storybook 的功能。
  • UI: Storybook 的用户界面,用于展示组件和控制 props。
  • Builders: 用于构建 Storybook 的静态资源,支持 Webpack、Vite 等构建工具。

Storybook 的工作原理大致如下:

  1. 加载 Stories: Storybook 会扫描项目中的 .stories.js.stories.jsx 文件,加载所有的 stories。
  2. 解析 Stories: Storybook 会解析每个 story,提取组件、props 和其他配置信息。
  3. 渲染组件: Storybook 会根据 story 的配置,渲染组件,并在 UI 中展示。
  4. 交互控制: 用户可以通过 Storybook 的 UI 修改组件的 props,触发事件,并查看组件的实时更新。
  5. 生成文档: Storybook 可以根据组件和 story 的信息,自动生成组件文档。

六、Storybook与其他工具的比较(知己知彼,百战不殆!)

工具 优点 缺点 适用场景
Storybook 组件独立开发、组件文档自动化、组件复用性提升、视觉测试、团队协作、丰富的 Addons、强大的 Controls、交互测试。 需要一定的学习成本、配置相对复杂 UI 组件库开发、设计系统搭建、组件文档生成、视觉回归测试、团队协作。
Styleguidist 自动生成组件文档、支持 Markdown 编写文档、配置简单。 功能相对简单、扩展性较差、不支持交互测试。 简单的组件文档生成、不需要复杂配置的场景。
Bit 组件共享、组件版本控制、组件依赖管理。 学习成本较高、配置复杂、需要搭建 Bit 服务器。 组件共享、组件版本控制、跨项目组件复用。
Docz 基于 MDX 的文档生成工具、支持 React 组件、配置简单。 功能相对简单、扩展性较差。 简单的组件文档生成、基于 MDX 的文档编写。

七、总结(希望你听完能有所收获!)

Storybook 是一款强大的 UI 组件开发工具,可以帮助你提高组件开发的效率、提升组件的复用性、自动化组件文档的生成、方便进行视觉测试、促进团队协作。 虽然它需要一定的学习成本和配置,但一旦掌握,你就能体会到它的强大之处。 希望通过今天的讲座,你能对 Storybook 有更深入的了解,并在实际项目中灵活运用,让你的组件开发更上一层楼!

好了,今天的讲座就到这里,感谢大家的收听! 如果大家还有什么问题,可以随时提问。下次有机会再和大家分享更多有趣的技术知识!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注