菜单

预览系统

相关源文件

预览系统负责在 Storybook UI 的 iframe 中独立渲染 stories,并与 Vitest 等测试框架集成。它处理 story 的执行、装饰器、args 管理和特定框架的渲染。该系统还转换 stories 以用于测试,并通过预览文件提供全面的配置。

有关控制预览的 Manager UI 的信息,请参阅 Manager UI 系统。有关特定框架渲染的详细信息,请参阅 框架渲染器

架构概述

预览系统由多个相互关联的组件组成,它们协同工作以渲染 stories 并与测试框架集成。

预览系统跨越多个上下文运行

  • 预览配置:配置文件定义了 stories 如何渲染、装饰和测试
  • 核心系统Preview 类通过 StoryStoreStoryRender 来协调 story 的加载和渲染
  • 测试集成:Vitest 插件使用 vitestTransformtestStory 工具将 stories 转换为测试
  • Story 处理:解析和索引 CSF 文件以创建 story 索引

关键集成点包括 .storybook/preview.tsx 中的预览配置、.storybook/main.ts 中的主配置以及用于测试集成的 Vitest 设置文件。

来源:code/.storybook/preview.tsx code/.storybook/main.ts code/core/src/preview-api/modules/preview-web/Preview.tsx code/addons/vitest/src/vitest-plugin/index.ts

Story渲染管道

当选择一个 story 进行显示时,它在预览 iframe 中渲染之前会经过一个多阶段的管道。

渲染过程有多个阶段

  1. 初始化:预览加载 story 元数据并准备环境
  2. Story 加载:Store 加载并使用 args 和 decorators 准备 story
  3. 渲染:特定框架的渲染器将 story 渲染到 canvas
  4. 更新:任何后续更新(如 args 更改)都会触发重新渲染

来源:code/core/src/preview-api/modules/preview-web/Preview.tsx400-450 code/renderers/vue3/src/render.ts43-112 code/renderers/react/src/preview.tsx code/addons/vitest/src/vitest-plugin/index.ts

框架渲染器

每个 UI 框架(React、Vue、Angular 等)都有自己的渲染器实现,可以知道如何为该框架渲染组件。这些渲染器实现了一个 renderToCanvas 函数,该函数接受一个 story 上下文并将其渲染到提供的 canvas 元素。

每个渲染器都有特定的逻辑来

  • 将组件挂载到 DOM
  • 处理 props/args
  • 管理组件生命周期
  • 框架特定功能(Vue 中的插槽、React 中的 JSX 等)

例如,Vue 渲染器为每个 story 创建一个 Vue 应用实例,而 React 渲染器使用 ReactDOM 来渲染元素。

来源:code/renderers/vue3/src/render.ts43-112 code/renderers/react/src/preview.tsx

预览配置

预览系统通过几个关键文件进行配置,这些文件定义了 stories 如何渲染、装饰以及如何与测试框架集成。

预览文件配置

.storybook/preview.tsx 文件定义了全局的 decorators、loaders、parameters 和 addons。

关键配置方面包括

  • Addons:与 addonDocsaddonThemesaddonA11yaddonTestaddonPseudoStates 的集成
  • Decorators:主题切换、DocsContext 供应和基于参数的样式设置
  • Loaders:为需要 CSF 文件数据的 blocks 设置 DocsContext
  • Parameters:docs、controls、themes 和 backgrounds 的全局设置

主配置

.storybook/main.ts 文件配置 story 的发现、框架设置和构建选项。

配置目的
storiesstory 文件模式和目录的数组
framework框架规范(例如,@storybook/react-vite
addons要加载的 addon 包列表
previewAnnotations其他预览配置文件
viteFinalVite 特定的构建配置

Vitest 设置配置

.storybook/storybook.setup.ts 文件配置了测试集成。

该设置文件使用特定于浏览器的测试工具配置项目注释,根据环境在 Vitest 的浏览器模式和 Storybook 的测试工具之间切换。

来源:code/.storybook/preview.tsx388-401 code/.storybook/main.ts17-169 code/.storybook/storybook.setup.ts11-28

预览初始化

预览系统通过一个引导过程进行初始化,该过程加载配置并设置渲染环境。

初始化过程

  1. 创建一个 Promise 来跟踪初始化的完成情况
  2. 从预览配置加载项目注释
  3. 运行项目注释中定义的任何 beforeAll hook
  4. /index.json 获取 story 索引
  5. 使用索引和注释创建 StoryStore
  6. 设置初始 globals 并解析初始化 promise

Preview 类提供了一个 ready() 方法,该方法返回初始化 promise,供需要等待完成的组件使用。

来源:code/core/src/preview-api/modules/preview-web/Preview.tsx79-137 code/core/src/preview-api/modules/preview-web/Preview.tsx169-217

源码生成

预览系统的一个重要功能是生成 story 的源代码以在文档中显示。每个框架都有自己的 source 装饰器,用于从渲染的 story 中提取或生成源代码。

框架源码生成方法
React使用 react-element-to-jsx-string 将 React 元素转换为 JSX
Vue从组件 props 和插槽生成源代码
Web Components从渲染输出中提取 innerHTML
HTML使用渲染的 HTML 字符串或元素 outerHTML
Svelte从组件属性和插槽内容生成代码

源码生成过程

  1. Story 由框架正常渲染
  2. 装饰器会拦截渲染输出
  3. 特定框架的逻辑会提取或生成源代码
  4. 源代码通过 emitTransformCode 发送,供文档显示

可以通过 story 参数自定义或禁用源码生成。

来源:code/renderers/react/src/docs/jsxDecorator.tsx code/renderers/vue3/src/docs/sourceDecorator.ts code/renderers/html/src/docs/sourceDecorator.ts code/core/src/preview-api/modules/preview-web/emitTransformCode.ts

Story 处理与索引

预览系统会处理 CSF 文件以创建可用 stories 及其元数据的索引。

Story 索引生成

StoryIndexGenerator 处理 story 文件以创建 story 索引。

CSF 文件处理

CsfFile 类解析单个 story 文件。

处理阶段目的
AST 解析将 JavaScript/TypeScript 解析为 AST
Meta 检测提取默认导出(meta)信息
Story 检测查找并处理命名的 story 导出
注解处理提取参数、args 和 decorators
Tag 处理合并来自 meta、story 和 preview 级别的标签

用于测试的 Story 转换

对于 Vitest 集成,stories 会经过额外的转换。

转换过程

  1. 解析 CSF 文件并提取 meta 和 story 信息
  2. 如果未指定,则根据文件路径生成自动标题
  3. 根据标签配置过滤 stories(includeexcludeskip
  4. 为每个有效的 story 生成测试调用
  5. 注入 Vitest 和测试工具所需的导入
  6. 添加文件守卫以防止跨文件测试执行

如果在过滤后未找到有效的 stories,则会生成一个 describe.skip() 块。

来源:code/core/src/core-server/utils/StoryIndexGenerator.ts code/core/src/csf-tools/CsfFile.ts377-464 code/core/src/csf-tools/vitest-plugin/transformer.ts103-281

与 Manager UI 的通信

预览系统通过双向通道与 Manager UI 进行通信,以实现更新 args、更改 globals 和同步状态等功能。

预览系统在初始化期间设置通道侦听器,并通过专用方法处理事件。

  • onUpdateArgs():更新 story 参数并触发重新渲染
  • onUpdateGlobals():更新全局值并发出更新后的 globals
  • onForceReRender():强制重新渲染所有活动的 story 渲染
  • onRequestArgTypesInfo():提供 controls 的 argTypes 信息

每个处理程序方法都会更新相应的状态,并将事件发回 Manager UI。

来源: code/core/src/preview-api/modules/preview-web/Preview.tsx139-147 code/core/src/preview-api/modules/preview-web/Preview.tsx298-395

渲染上下文

每次故事渲染操作都会接收一个上下文对象,其中包含正确渲染故事所需的所有信息。

上下文对象提供

  • 故事元数据(id、name、title)
  • 组件引用
  • 当前 args 和 argTypes
  • 全局变量
  • 故事参数
  • 故事函数和原始故事函数
  • 渲染控制函数

这些上下文对象会通过渲染流程进行传递,并可用于装饰器、故事函数和渲染器。

来源: code/renderers/vue3/src/render.ts43-46 code/renderers/react/src/docs/jsxDecorator.tsx207-218

Vitest 集成

Preview 系统集成了 Vitest,可以将故事作为可执行的测试用例进行测试。

故事转换流程

故事通过一个多阶段流程转换为 Vitest 测试

插件配置

storybookTest 插件配置 Vitest 以便与 Storybook 协同工作

配置选项目的
configDir.storybook 配置目录的路径
storybookScript用于在监视模式下启动 Storybook 的可选脚本
storybookUrlStorybook 托管的 URL,用于测试输出链接
tags.include要在测试中包含的标签(默认值:['test']
tags.exclude要从测试中排除的标签
tags.skip要跳过的标签(测试会运行,但会标记为已跳过)

测试执行

testStory 函数将单个故事执行为测试

测试执行过程

  1. 将故事与项目注解和初始全局变量组合在一起
  2. 根据标签检查是否应跳过故事
  3. 根据故事参数设置视口
  4. 运行故事(包括 play 函数)
  5. 收集测试报告并附加故事元数据

故事文件转换

vitestTransform 函数将 CSF 文件转换为测试文件

  • 添加 Vitest 导入(testexpect
  • 添加 Storybook 测试实用工具导入
  • 将每个故事导出转换为 test() 调用
  • 添加文件守卫以防止跨文件测试执行
  • 根据标签配置过滤故事

例如,一个故事像这样

变成

来源: code/addons/vitest/src/vitest-plugin/index.ts107-446 code/core/src/csf-tools/vitest-plugin/transformer.ts35-281 code/addons/vitest/src/vitest-plugin/test-utils.ts33-65

性能考量

Preview 系统包含优化措施,以确保高效的故事渲染

  1. 惰性加载:故事在被选中时按需加载
  2. Args 缓存:Args 更新会尽量减少重新渲染
  3. 渲染阶段:渲染过程分为几个阶段(init、prepare、render)
  4. 清理:每次渲染操作都会返回一个拆卸函数,用于清理资源

对于大型故事或复杂组件,Preview 系统可能需要强制重新挂载(完全重新渲染),而不仅仅是更新 props,尤其是在以下情况:

  • 组件结构发生显著变化
  • 选择了在 play 函数中具有 mount 的故事
  • 触发了 FORCE_REMOUNT 事件

来源: code/renderers/vue3/src/render.ts46-65 code/core/src/preview-api/modules/preview-web/Preview.tsx337-358

错误处理

Preview 系统包含强大的错误处理机制,用于捕获和显示故事渲染过程中发生的错误。

错误在不同级别得到处理

  • 组件渲染错误会被捕获和显示
  • Play 函数错误会被捕获并在交互面板中显示
  • 故事加载错误会附带上下文信息显示
  • 配置错误会显示详细消息

这确保了在一个故事中的错误不会导致整个 Storybook 实例崩溃,并为开发者提供了清晰的错误信息。

来源: code/renderers/vue3/src/render.ts89-104 code/core/src/preview-api/modules/preview-web/Preview.tsx162-165