预览系统负责在 Storybook UI 的 iframe 中独立渲染 stories,并与 Vitest 等测试框架集成。它处理 story 的执行、装饰器、args 管理和特定框架的渲染。该系统还转换 stories 以用于测试,并通过预览文件提供全面的配置。
有关控制预览的 Manager UI 的信息,请参阅 Manager UI 系统。有关特定框架渲染的详细信息,请参阅 框架渲染器。
预览系统由多个相互关联的组件组成,它们协同工作以渲染 stories 并与测试框架集成。
预览系统跨越多个上下文运行
Preview 类通过 StoryStore 和 StoryRender 来协调 story 的加载和渲染vitestTransform 和 testStory 工具将 stories 转换为测试关键集成点包括 .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 进行显示时,它在预览 iframe 中渲染之前会经过一个多阶段的管道。
渲染过程有多个阶段
来源: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 元素。
每个渲染器都有特定的逻辑来
例如,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。
关键配置方面包括
addonDocs、addonThemes、addonA11y、addonTest 和 addonPseudoStates 的集成.storybook/main.ts 文件配置 story 的发现、框架设置和构建选项。
| 配置 | 目的 |
|---|---|
stories | story 文件模式和目录的数组 |
framework | 框架规范(例如,@storybook/react-vite) |
addons | 要加载的 addon 包列表 |
previewAnnotations | 其他预览配置文件 |
viteFinal | Vite 特定的构建配置 |
.storybook/storybook.setup.ts 文件配置了测试集成。
该设置文件使用特定于浏览器的测试工具配置项目注释,根据环境在 Vitest 的浏览器模式和 Storybook 的测试工具之间切换。
来源:code/.storybook/preview.tsx388-401 code/.storybook/main.ts17-169 code/.storybook/storybook.setup.ts11-28
预览系统通过一个引导过程进行初始化,该过程加载配置并设置渲染环境。
初始化过程
Promise 来跟踪初始化的完成情况beforeAll hook/index.json 获取 story 索引StoryStorePreview 类提供了一个 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 | 从组件属性和插槽内容生成代码 |
源码生成过程
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
预览系统会处理 CSF 文件以创建可用 stories 及其元数据的索引。
StoryIndexGenerator 处理 story 文件以创建 story 索引。
CsfFile 类解析单个 story 文件。
| 处理阶段 | 目的 |
|---|---|
| AST 解析 | 将 JavaScript/TypeScript 解析为 AST |
| Meta 检测 | 提取默认导出(meta)信息 |
| Story 检测 | 查找并处理命名的 story 导出 |
| 注解处理 | 提取参数、args 和 decorators |
| Tag 处理 | 合并来自 meta、story 和 preview 级别的标签 |
对于 Vitest 集成,stories 会经过额外的转换。
转换过程
include、exclude、skip)如果在过滤后未找到有效的 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 进行通信,以实现更新 args、更改 globals 和同步状态等功能。
预览系统在初始化期间设置通道侦听器,并通过专用方法处理事件。
onUpdateArgs():更新 story 参数并触发重新渲染onUpdateGlobals():更新全局值并发出更新后的 globalsonForceReRender():强制重新渲染所有活动的 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
每次故事渲染操作都会接收一个上下文对象,其中包含正确渲染故事所需的所有信息。
上下文对象提供
这些上下文对象会通过渲染流程进行传递,并可用于装饰器、故事函数和渲染器。
来源: code/renderers/vue3/src/render.ts43-46 code/renderers/react/src/docs/jsxDecorator.tsx207-218
Preview 系统集成了 Vitest,可以将故事作为可执行的测试用例进行测试。
故事通过一个多阶段流程转换为 Vitest 测试
storybookTest 插件配置 Vitest 以便与 Storybook 协同工作
| 配置选项 | 目的 |
|---|---|
configDir | .storybook 配置目录的路径 |
storybookScript | 用于在监视模式下启动 Storybook 的可选脚本 |
storybookUrl | Storybook 托管的 URL,用于测试输出链接 |
tags.include | 要在测试中包含的标签(默认值:['test']) |
tags.exclude | 要从测试中排除的标签 |
tags.skip | 要跳过的标签(测试会运行,但会标记为已跳过) |
testStory 函数将单个故事执行为测试
测试执行过程
vitestTransform 函数将 CSF 文件转换为测试文件
test,expect)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 系统包含优化措施,以确保高效的故事渲染
对于大型故事或复杂组件,Preview 系统可能需要强制重新挂载(完全重新渲染),而不仅仅是更新 props,尤其是在以下情况:
mount 的故事FORCE_REMOUNT 事件来源: code/renderers/vue3/src/render.ts46-65 code/core/src/preview-api/modules/preview-web/Preview.tsx337-358
Preview 系统包含强大的错误处理机制,用于捕获和显示故事渲染过程中发生的错误。
错误在不同级别得到处理
这确保了在一个故事中的错误不会导致整个 Storybook 实例崩溃,并为开发者提供了清晰的错误信息。
来源: code/renderers/vue3/src/render.ts89-104 code/core/src/preview-api/modules/preview-web/Preview.tsx162-165