文档
Storybook Docs

编写预设插件

Storybook 预设是预先配置的设置或配置,它使开发人员能够快速设置和自定义他们的环境,并具有特定的功能、能力或集成。

预设如何工作

预设插件允许开发人员通过 API 组合各种配置选项和插件,以与 Storybook 集成并自定义其行为和功能。通常,预设分为两个文件,每个文件都有其特定的作用。

本地预设

这种类型的预设允许您封装和组织特定于插件的配置,包括构建器支持、Babel或第三方集成。例如:

example-addon/src/preset.ts
import { webpackFinal as webpack } from './webpack/webpackFinal';
 
import { viteFinal as vite } from './vite/viteFinal';
 
import { babelDefault as babel } from './babel/babelDefault';
 
export const webpackFinal = webpack as any;
 
export const viteFinal = vite as any;
 
export const babelDefault = babel as any;

根级预设

这种类型的预设面向用户,负责在用户无需任何额外配置的情况下注册插件,通过捆绑 Storybook 相关的特性(例如参数)通过 previewAnnotations 和 UI 相关的特性(例如插件)通过 managerEntries API。例如:

example-addon/preset.js
export const previewAnnotations = [import.meta.resolve('./dist/preview')];
 
export const managerEntries = [import.meta.resolve('./dist/manager')];
 
export * from './dist/preset.js';

预设 API

在编写预设时,您可以访问一组精选的 API 来与 Storybook 环境进行交互,包括支持的构建器(例如 Webpack、Vite)、Storybook 配置和 UI。以下是您在编写预设插件时可用的 API。

Babel

要自定义 Storybook 的 Babel 配置并添加对其他特性的支持,您可以使用 babelDefault API。它将在任何其他用户预设之前应用提供的配置,而最终用户可以通过 babel 配置选项进一步自定义。例如:

example-addon/src/babel/babelDefault.ts
import { TransformOptions } from '@babel/core';
 
export function babelDefault(config: TransformOptions) {
  return {
    ...config,
    plugins: [
      ...config.plugins,
      [import.meta.resolve('@babel/plugin-transform-react-jsx'), {}, 'preset'],
    ],
  };
}

Babel 配置仅应用于内部使用 Babel 的框架。如果您为使用不同编译器(如SWCesbuild)的框架启用它,它将被忽略。

构建器

默认情况下,Storybook 支持领先的行业构建器,包括WebpackVite。如果您需要这些构建器的任何附加功能,可以使用 API 根据您的具体需求扩展构建器配置。

Vite

如果您正在创建预设并希望包含 Vite 支持,可以使用 viteFinal API 来修改默认配置并启用其他功能。例如:

example-addon/src/vite/viteFinal.ts
export function ViteFinal(config: any, options: any = {}) {
  config.plugins.push(
    new MyCustomPlugin({
      someOption: true,
    }),
  );
 
  return config;
}

Webpack

要自定义 Storybook 中的 Webpack 配置以支持其他文件类型、应用特定加载器、配置插件或进行任何其他必要的修改,您可以使用 webpackFinal API。调用后,它将使用提供的配置扩展默认 Webpack 配置。例如:

example-addon/src/webpack/webpackFinal.ts
import { fileURLtoPath } from 'node:url';
import type { Configuration as WebpackConfig } from 'webpack';
 
export function webpackFinal(config: WebpackConfig, options: any = {}) {
  const rules = [
    ...(config.module?.rules || []),
    {
      test: /\.custom-file$/,
      loader: fileURLToPath(import.meta.resolve(`custom-loader`)),
    },
  ];
  config.module.rules = rules;
 
  return config;
}

ManagerEntries

如果您正在编写一个预设来加载第三方插件,而您可能无法控制它们,但需要访问特定功能或其他配置,您可以使用 managerEntries API。例如:

example-addon/preset.js
export const managerEntries = (entry = []) => {
  return [...entry, import.meta.resolve('path-to-third-party-addon')];
};

PreviewAnnotations

如果您需要额外的设置来为预设渲染故事,例如装饰器参数,您可以使用 previewAnnotations API。例如,要将装饰器应用于所有故事,请创建一个包含装饰器的预览文件,并将其提供给预设,如下所示:

example-addon/src/preview.ts
import type { Renderer, ProjectAnnotations } from 'storybook/internal/types';
import { PARAM_KEY } from './constants';
import { CustomDecorator } from './decorators';
 
const preview: ProjectAnnotations<Renderer> = {
  decorators: [CustomDecorator],
  globals: {
    [PARAM_KEY]: false,
  },
};
 
export default preview;

高级配置

预设 API 的设计旨在保持灵活性,并允许您根据特定需求自定义 Storybook,包括为更高级的用例使用预设而不发布它们。在这种情况下,您可以依赖私有预设。这些私有预设包含用于开发目的的配置选项,而不是面向最终用户。 .storybook/main.js|ts 文件就是这种私有预设的一个示例,它可以让您修改 Storybook 的行为和功能。

.storybook/main.ts
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, angular, etc.
import type { StorybookConfig } from '@storybook/your-framework';
 
const config: StorybookConfig = {
  viteFinal: async (config, options) => {
    // Update config here
    return config;
  },
  webpackFinal: async (config, options) => {
    // Change webpack config
    return config;
  },
  babel: async (config, options) => {
    return config;
  },
};
 
export default config;

插件

对于插件的消费者来说,managerEntries API 可能过于技术化,难以使用。为了更轻松地将插件添加到 Storybook,预设 API 提供了 addons API,它接受一个插件名称数组,并为您自动加载它们。例如:

.storybook/main.ts
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
 
const config: StorybookConfig = {
  addons: [
    // Other Storybook addons
    '@storybook/addon-a11y',
  ],
};
 
export default config;

值数组支持对其他预设和应包含在管理器中的插件的引用。Storybook 会自动检测提供的值是预设还是插件,并相应地加载它。

Entries

Entries 是注册预览入口点的地方。此功能可用于创建 configure-storybook 预设,该预设可自动将所有 *.stories.js 文件加载到 Storybook 中,从而无需用户反复复制粘贴相同的配置。

UI 配置

Storybook 预设 API 还允许访问 UI 配置,包括预览的 headbody HTML 元素,通过 previewHeadpreviewBody API 进行配置。两者都允许您以类似于使用 preview-head.htmlpreview-body.html 文件的方式来设置 Storybook。这些方法接受一个字符串并返回一个修改后的版本,将提供的内容注入到 HTML 元素中。

.storybook/main.ts
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
 
const config: StorybookConfig = {
  previewBody: (body) => `
    ${body}
    ${
      process.env.ANALYTICS_ID ? '<script src="https://cdn.example.com/analytics.js"></script>' : ''
    }
  `,
};
 
export default config;

此外,如果您需要自定义管理器(即 Storybook 的搜索、导航、工具栏和插件的渲染位置),您可以使用 managerHead 来修改 UI,这与使用 manager-head.html 文件的方式类似。例如:

.storybook/main.ts
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
 
const config: StorybookConfig = {
  managerHead: (head) => `
    ${head}
    <link rel="icon" type="image/png" href="/logo192.png" sizes="192x192" />
  `,
};
 
export default config;

但是,如果您需要,您也可以自定义 Storybook 用于渲染 UI 的模板。为此,您可以使用 previewMainTemplate API 并提供一个作为 ejs 文件创建的自定义模板的引用。有关如何执行此操作的示例,请参阅 Webpack 5 构建器使用的模板

故障排除

Storybook 未加载我的预设文件

由于 Storybook 使用esbuild而不是 Webpack 来构建 UI,因此依赖 managerWebpack API 来配置管理器或加载 CSS 或图像以外的其他文件的预设将不再有效。我们建议将其从您的预设中删除,并调整您的配置以将任何其他文件转换为 JavaScript。

了解更多关于 Storybook 插件生态系统的知识