加入直播会话:美国东部时间周四上午 11 点,Storybook 9 发布及问答

性能

一个 Storybook 插件,帮助更好地理解和调试 React 组件的性能

在 Github 上查看

一个 Storybook 插件,帮助更好地理解和调试 React 组件的性能

📺 项目概述Jack Herrington 提供

亮点 🌟

  • 零配置(交互除外):无需任何配置即可生成与服务器端渲染和客户端挂载相关的性能信息
  • 固定结果:您可以运行某些任务,固定结果,进行更改,然后重新运行任务并查看更改了什么
  • 保存/加载结果:您可以运行某些任务,将结果保存为本地文件,稍后通过将该文件加载回插件来再次运行它们。
  • 交互:添加您自己的自定义用户交互作为故事的参数运行。这让您可以测量交互所需的时间。此 API 超级灵活且强大!
  • 控制:运行所有任务以获得概览,或运行单个任务以深入研究特定问题
  • 标记:所有任务都使用 User Timing API 进行标记,以便在浏览器的性能分析器中轻松调试单个任务

安装

  1. 安装 storybook-addon-performance
# pnpm
pnpm add storybook-addon-performance --dev

# yarn
yarn add storybook-addon-performance --dev

# npm
npm install storybook-addon-performance --save-dev
  1. .storybook/main.js 中注册插件
module.exports = {
  addons: ['storybook-addon-performance'],
};
  1. 添加装饰器

您可以将装饰器全局添加到 .storybook/preview.js 中的每个故事中(推荐)

import { withPerformance } from 'storybook-addon-performance';

export const decorators = [withPerformance];

或者您可以将其添加到单个故事中

使用 组件故事格式 (CSF)

import MyComponent from './MyComponent';
import { withPerformance } from 'storybook-addon-performance';

export default {
  title: 'MyComponent',
  component: MyComponent,
  decorators: [withPerformance],
};

使用 StoriesOf API

import MyComponent from './MyComponent';
import { withPerformance } from 'storybook-addon-performance';

storiesOf('MyComponent', module)
  .addDecorator(withPerformance)
  .add('MyComponent', () => <MyComponent />);

用法:交互

交互任务是一种可以在单个故事基础上定义和运行的任务类型。它们对于测量组件的交互性能非常有用。

要定义您的交互任务,首先创建一个对象数组,每个对象包含任务的 namedescription(可选),以及一个执行您希望测量的任务的 run 函数。

import { InteractionTaskArgs, PublicInteractionTask } from 'storybook-addon-performance';
import { findByText, fireEvent } from '@testing-library/dom';

// ...

const interactionTasks: PublicInteractionTask[] = [
  {
    name: 'Display dropdown',
    description: 'Open the dropdown and wait for Option 5 to load',
    run: async ({ container }: InteractionTaskArgs): Promise<void> => {
      const element: HTMLElement | null = container.querySelector('.addon__dropdown-indicator');
      invariant(element);
      fireEvent.mouseDown(element);
      await findByText(container, 'Option 5', undefined, { timeout: 20000 });
    },
  },
];

每个任务对象中的 run 函数接受两个参数

  • container:一个 HTMLElement 容器,包含故事组件的渲染实例

  • controls:包含一个异步计时函数,可以可选地调用它来指定何时开始和结束测量;否则将测量完成整个 run 函数所需的时间。当任务涉及一些设置工作时很有用。

    要使用,请将相关操作包裹在 controls.time 中,如下所示

    run: async ({ container }: InteractionTaskArgs): Promise<void> => {
      // setup
      await controls.time(async () => {
        // interaction task you'd like to measure
      });
    };
    

请注意,您可以使用任何您喜欢的库来执行这些交互测试——上面的示例使用 @testing-library/dom 来打开示例中的 select 并等待特定项。

然后,您可以将交互任务数组包含在故事的 performance 参数中,键为 interactions

// Using the Component Story Format (CSF)
// https://storybook.org.cn/docs/formats/component-story-format/
import { findByText, fireEvent } from '@testing-library/dom';
import { PublicInteractionTask } from 'storybook-addon-performance';
import React from 'react';
import Select from 'react-select';
import invariant from 'tiny-invariant';

export default {
  title: 'React select example',
};

const interactionTasks: PublicInteractionTask[] = [
  {
    name: 'Display dropdown',
    description: 'Open the dropdown and wait for Option 5 to load',
    run: async ({ container }: InteractionTaskArgs): Promise<void> => {
      const element: HTMLElement | null = container.querySelector('.addon__dropdown-indicator');
      invariant(element);
      fireEvent.mouseDown(element);
      await findByText(container, 'Option 5', undefined, { timeout: 20000 });
    },
  },
];

select.storyName = 'React Select';
select.parameters = {
  performance: {
    interactions: interactionTasks,
  },
};

提供的类型

如上所示,该插件导出了两个类型定义,以帮助您创建自己的交互任务

  • PublicInteractionTask:定义交互任务的对象结构;如上所示,将这些任务数组作为参数传递给 Storybook。
  • InteractionTaskArgs:交互任务 run 函数的参数

用法:保存和加载结果

您可以使用保存 API 将性能任务的结果保存为本地文件。保存 API 会创建一个特定于故事的文件,稍后可以加载该文件作为基准。这对于 CI 或测试分支与主干的更改非常有用。您可以通过 UI 中的“保存结果”/“加载结果”按钮使用此 API。

此 API 的一些注意事项

  • Storybook 运行的性能结果是可变的,并可能在测试运行时根据 CPU 利用率/内存而变化。如果您打算保存文件,请确保您在与原始运行环境尽可能相似的环境中重新运行/比较结果。
  • 为了使此 API 正常工作,任务文件应基于与原始测试相同的样本/副本数量。

为了获得更一致的结果,我们建议使用 10 个副本/10 个样本记录文件。

用法:过滤任务组

有些组件设计时并未考虑在服务器端渲染或客户端上运行。为了支持这一点,我们创建了一个允许列表,您可以选择传入该列表,以仅允许运行您想要的组。要配置此选项,请将 allowedGroups 选项设置为故事参数的一部分。

  • 默认值:['server', 'client'] (运行所有任务)
// Using [Component Story Format (CSF)](https://storybook.org.cn/docs/formats/component-story-format/)
export const onlyClient = () => <p>A story only measuring client-side performance 👩‍💻</p>;

onlyClient.parameters = {
  performance: {
    allowedGroups: ['client'],
  },
};

export const onlyServer = () => <p>A story only measuring server-side performance ‍☁️</p>;

onlyServer.parameters = {
  performance: {
    allowedGroups: ['server'],
  },
};

关于性能指标的注意事项 💡

为了获得最准确的性能指标,您应该使用 Storybook 的生产构建。有关更多背景信息,请参阅 React 优化性能文档

虽然这个插件在开发构建中也能工作,但结果会更不稳定。

本地插件开发

在 storybook-addon-performance 文件夹中 (packages/storybook-addon-performance)

# Start the typescript watcher and a local storybook:
pnpm dev

# Start just the typescript watcher
# This is needed as storybook does not compile addons
pnpm typescript:watch

# Start the local storybook
pnpm storybook:dev

感谢

由您的 Atlassian 朋友们用 ❤️ 制作

With ❤️ from Atlassian

制作人
  • alexreardon
    alexreardon
  • darkpurple141
    darkpurple141
  • harshai
    harshai
  • andrewcampbell
    andrewcampbell
标签