效能

一個 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 來開啟範例中的選取器,並等待特定項目。

然後,您可以將互動任務陣列包含在故事的 performance 參數中,並使用 interactions 作為鍵

// Using the Component Story Format (CSF)
// https://storybook.dev.org.tw/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 或測試分支中與 trunk 的變更非常有用。您可以透過 UI 中的「儲存結果/載入結果」按鈕使用此 API。

此 API 的一些注意事項

  • Storybook 執行效能結果是可變的,並且可能會因測試執行時的 CPU 使用率/記憶體而異。如果您打算儲存工件,請確保您在與原始執行環境盡可能相似的環境中重新執行/比較結果。
  • 為了使此 API 正常運作,任務工件應基於與原始測試相同數量的樣本/複製。

為了獲得更一致的結果,我們建議使用 10 個副本/10 個樣本記錄工件。

使用方式:篩選任務群組

某些元件並非設計為在伺服器端渲染或用戶端上運作。為了支援這一點,我們建立了一個允許清單,您可以選擇性地傳遞該清單,以僅允許執行您想要的群組。若要設定此選項,請將 allowedGroups 選項設定為故事參數的一部分。

  • 預設值:['server', 'client'] (執行所有項目)
// Using [Component Story Format (CSF)](https://storybook.dev.org.tw/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
標籤