返回博客

Storybook 按需架构

构建的 Storybook 体积缩小 3 倍,加载时间更快

loading
Tom Coleman
@tmeasday
最近更新

随着 stories 数量的增长,以高性能的方式加载所有 stories 变得越来越棘手。这最终会拖累开发者体验。我们使用 Storybook 构建 Storybook,因此我们也感同身受。

在最近的版本中,性能已成为首要任务。最新版本在构建时间和包大小方面包含了渐进但明显的改进。

我很高兴分享 Storybook 全新的按需架构,这是 6.4 版本的一项根本性变革,它改进了已构建 Storybook 的性能。我们与 Webpack 和 Shopify UX 工程团队合作,将包大小减少了多达三倍。请继续阅读以了解其原理。

幕后原理

在我们开始之前,让我们回顾一下幕后发生了什么。Storybook 是组件示例(称为 stories)的集合。这些是小的 Javascript 代码片段,用于隔离渲染 UI 组件(通常是设计系统或应用程序的一部分)。

Stories 在 CSF 文件(Component Story Format,组件故事格式)中定义。与组件相关的所有 stories 都分组在同一个文件中。Storybook 的工作是获取在 CSF 文件中定义的 story,并在浏览器中根据用户请求进行渲染。

为了渲染这些示例,我们需要在浏览器中加载所有相关的代码。为此,我们使用 Webpack 创建一个 JavaScript 包,其中包含:所有 CSF 文件、您的组件以及渲染它们所需的资源,以及 Storybook 的运行时。

由于包大小对性能有巨大影响,因此我们将工作重点放在缩小包大小上。

如何将 Storybook 的包大小减半

当涉及到性能时,更小 = 更快。我们 Storybook 的包越小,加载速度就越快。考虑到这一点,我们进行了两项架构更改,以加快您的开发者体验

  • 代码拆分:为生产环境 Storybook 启用更快的加载时间
  • 智能文件系统缓存:启用更快的开发启动速度

在以前版本的 Storybook 中,所有代码都被打包成一个大包。更多的组件和 stories 导致包变得更大,从而减慢了 Storybook 的速度。启动(尤其是通过网络加载时)或启动开发服务器需要一段时间。

近年来,更大的应用程序开始依赖包拆分。其思想是将大包拆分成更小、更易于管理的部分。此外,像 NextJS 这样的工具率先使用了懒加载编译技术。在启动时构建整个应用程序需要时间。相反,它们只构建用户专注于特定任务所需的特定模块。

包拆分的关键是仅加载首次渲染所需的代码。其他所有内容都会在需要时异步获取(通过 import() 构造)。

应用程序通过手动指定和等待 import() 或通过在页面路由上自动拆分(如 NextJS 所做的那样)来实现这一点。第一种选择更手动,让您可以更好地控制体验。第二种选择可以在框架级别进行优化,但通常会限制您可以执行的操作。

对于 Storybook,我们与 Webpack 团队合作探索了这两种方法。

无效的方法:手动 import() 函数

自 Storybook 6.1 以来,可以使用 import() 函数来代码拆分您的 Storybook——使用实验性功能:loaders。

// A CSF file that establishes a import "boundary" to the component file
 
export default {
 title: "MyComponent",
 loaders: [async () => ({ Component: await import('./MyComponent') })],
 // In CSFv3 you could define this render() function for all components
 render: (args, { loaded: { Component } }) => <Component {...args} />,
};
 
export const MyStory = {
 args: { arg1: 'value' }
};

在上面的 CSF 文件中,没有直接静态导入 ./MyComponent;只是在提供的 loader 内部有一个异步 import()

通过这种设置,所有 CSF 文件都会创建一个单独的(初始)包。而每个组件文件都将形成自己的包,以及其依赖项。

在对此方法进行原型设计时,我们发现了两个主要缺点

  1. 要求用户为每个组件编写 loader 是笨拙的、不直观的,并且使 stories 更难重用。代码拆分似乎是一个优化细节,您作为 Storybook 的用户,不应该需要关心。
  2. 在实验中,我们经常发现包含所有 CSF 文件的初始包占 Storybook 总大小的很大一部分,从而降低了代码拆分的好处。

初始包之所以很大,通常是因为很难保持 CSF “纯粹”,不受其他组件依赖项的影响。此外,在 Storybook 嵌入或组合到其他上下文中的情况下,最小化初始包大小尤其重要。

有效的方法:自动代码拆分

另一种方法是让 Storybook 的 store 将每个 CSF 文件视为单独的异步 import(),并“按需”加载 stories

通过这种方式,每个 CSF 都会生成自己的包——组件加上加载和渲染 story 所需的最小依赖项。无需更改代码。这一切都在幕后发生,无需用户干预。

这种方法更复杂,并且在使用位置和时间方面存在一些注意事项(见下文)。但通常适用于即使是最复杂的 Storybook,只需进行最少的更改。

此行为将成为 Storybook 7.0 中的默认行为——它在 6.4 版本中通过 storyStoreV7 功能标志提供(见下文);之前的单包行为在 6.4 安装中仍然默认启用。

6.4 版本中的性能提升

引入代码拆分的主要目的是提高 Storybook 的性能。也就是说,安装和启动所需的时间,以及下载已构建 Storybook 并与之交互所需的时间。

6.4 版本中的更改侧重于在 Storybook 中启用代码拆分。直接影响将是更小的包大小,这意味着已构建的 Storybook 应该加载得更快。

例如,Chromatic Storybook(一个包含 2000 个 stories 的大型 Storybook)在升级到 v7 store 时显示了以下行为

同样,Shopify 的 Storybook 在启用 v7 store 后,初始包大小节省了 67.5%。

6.5+ 版本中性能方面的下一步是什么?

仅代码拆分不一定会改善使用 Storybook 的开发者体验。生成多个代码拆分包甚至可能比创建一个大包花费更长的时间。这取决于跨包的代码重复以及优化内容的复杂性。

然而,它解锁了进一步的优化。一个关键的优化是使用懒加载编译,仅生成渲染当前屏幕上可见 stories 所需的包。懒加载编译是 Webpack 5 的一项实验性功能,概念上类似于 NextJS 的即时页面构建。

懒加载编译和文件系统缓存的实验表明,在大型项目中,应该可以将开发启动时间和重建时间减少 3-5 倍。这将是 Storybook 6.5 的主要关注点。

此外,Webpack 拆分机制的其他优化现在也已解锁。我们鼓励用户尝试调整 Storybook 的默认 Webpack 设置,并将改进贡献回 6.5 版本。

注意事项

自动代码拆分方法棘手的部分在于,我们不再在“启动”时加载所有 CSF 文件。相反,我们需要从节点上下文中静态计算 Storybook 的 stories 列表(“Story Index”)。这意味着我们不评估您的 story 文件,而只是解析它们并分析生成的 AST。这限制了您可以在 CSF 文件中执行的操作(其中一些限制我们可能会在未来的迭代中删除)

  • 仅支持 CSF 格式 (v1-v3);不支持 storiesOf()
  • CSF 标题和 story 名称必须是静态定义的(即 title: 'Component',而不是 title: MyTitle)。
  • 自定义 storySort 函数具有更有限的 API。

今天就试试

自动代码拆分现已在 6.4 beta 版中提供。只需一分钟即可试用,您可以在项目的根目录中运行以下命令

npx sb upgrade --prerelease

如果您尚未使用 Storybook,则很容易上手

npx sb@next init

然后启用功能标志

// .storybook/main.js
module.exports = {
  features: {
    storyStoreV7: true,
  }
};

帮助塑造下一代 Storybook!

Storybook 按需架构带来了显着的性能优势,并允许您尝试其他 Webpack 优化。

开发者每天使用 Storybook 构建数百个组件和数千个 stories。您进行了哪些调整来加速您的 Storybook?我们很乐意听取您的意见。请在 Twitter 上联系我们,或访问 Storybook Discord

按需架构功能由 Tom Coleman(我!)、Juho VepsäläinenMichael Shilman 开发,并得到了整个 Storybook 社区的反馈。

Storybook 是 1320 多名社区贡献者的成果,并由顶级维护者的指导委员会组织。您也可以贡献新功能、修复错误或改进文档。加入我们的  Discord,在 Open Collective 上支持我们,或者直接参与 GitHub

加入 Storybook 邮件列表

获取最新的新闻、更新和发布

6,730位开发者及更多

我们正在招聘!

加入 Storybook 和 Chromatic 背后的团队。构建被成千上万开发者在生产环境中使用的工具。远程优先。

查看职位

热门文章

交互式 stories (beta 版)

使用 play 函数模拟用户行为
loading
Varun Vachhar

开始使用 Storybook 和 Next.js

通过四个简单的步骤将 Storybook 与 Next.js 集成
loading
Michael Chan

UI 测试手册

一种不会拖慢您速度的测试工作流程
loading
Varun Vachhar
加入社区
6,730位开发者及更多
为什么为什么选择 Storybook组件驱动的 UI
文档指南教程更新日志遥测
社区插件参与进来博客
展示探索项目组件术语表
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI