资讯动态

Storybook 全局装饰器(Global Decorators)实战指南:在 .storybook/preview 中统一包装所有 Story

发布时间:2026/9/10 15:48:06 来源:尧图企业网站定制
Storybook 全局装饰器Global Decorators实战指南在 .storybook/preview 中统一包装所有 Story【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook导读全局装饰器Global Decorators是 Storybook 在.storybook/preview文件中声明的一类装饰器可以作用于项目中每一个 Story是统一注入布局、间距、Provider、主题或 Mock 数据的最直接手段。本文以当前仓库 Storybook 源码为据系统讲解全局装饰器的定义方式、跨框架Angular / React / Solid / Svelte / Vue / Web Components的写法差异、与组件级、Story 级装饰器的执行顺序以及底层渲染机制。一、什么是装饰器与全局装饰器装饰器Decorator是一种将 Story 包裹在额外渲染功能中的机制。在 docs/writing-stories/decorators.mdx 中Storybook 官方将其定义为装饰器用于给 Story 包裹额外的标记markup或上下文 Mock许多插件addon正是通过定义装饰器来增强 Story 的渲染行为。按照作用范围Storybook 装饰器分为三层层级定义位置作用范围全局装饰器.storybook/preview.ts\|tsx中的decorators导出项目中所有 Story组件级装饰器CSF 默认导出default export的decorators键该组件下的所有 StoryStory 级装饰器CSF 命名导出named export的decorators键单个 Story本文聚焦第一层——全局装饰器。根据 docs/configure/index.mdx 的说明.storybook/preview.js负责控制 Story 的渲染方式它被加载到渲染组件预览的 Canvas iframe 中其decorators导出即全局装饰器数组。除decorators外preview文件还可以导出parameters全局参数和globalTypes全局类型定义。二、在不同框架中定义全局装饰器关联文档 docs/_snippets/storybook-preview-global-decorator.md 提供了 6 大渲染器下的完整示例。核心场景是当组件顶到边缘时用一个带margin: 3em的包裹层为所有 Story 增加间距harness。2.1 ReactCSF 3import React from react; // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc. import type { Preview } from storybook/your-framework; const preview: Preview { decorators: [ (Story) ( div style{{ margin: 3em }} {/* Decorators in Storybook also accept a function. Replace Story/ with Story() to enable it */} Story / /div ), ], }; export default preview;在 React 中装饰器接收一个Story组件并返回 JSX。官方注释特别提醒装饰器也接受函数形式将Story /替换为Story()即可启用函数调用方式以配合某些需要直接调用渲染函数的场景。JS 版本.storybook/preview.jsx写法与 TS 版本等价。2.2 Angularimport { type Preview, componentWrapperDecorator } from storybook/angular; const preview: Preview { decorators: [componentWrapperDecorator((story) div stylemargin: 3em${story}/div)], }; export default preview;Angular 框架推荐使用componentWrapperDecorator工具函数它接收一个返回模板字符串的回调${story}为被包裹的 Story 占位符从而以 Angular 模板语法完成包裹。2.3 Svelte// Replace your-framework with the framework you are using, e.g. sveltekit or svelte-vite import type { Preview } from storybook/your-framework; import MarginDecorator from ./MarginDecorator.svelte; const preview: Preview { decorators: [() MarginDecorator], }; export default preview;Svelte 略有不同需要先创建一个独立的 Svelte 组件作为装饰器如MarginDecorator.svelte再在decorators中返回该组件。如需向装饰器组件传递 props可返回包含Component与props键的对象以便根据 Story 上下文如parameters.pageLayout动态定制行为。2.4 VueCSF 3 与 CSF Nextimport type { Preview } from storybook/vue3-vite; const preview: Preview { decorators: [ (story) ({ components: { story }, template: div stylemargin: 3em;story //div, }), ], }; export default preview;Vue 装饰器返回一个组件选项对象将 Story 注册为局部组件story并在模板中渲染。CSF Next 实验性语法则使用definePreview包裹import { definePreview } from storybook/vue3-vite; export default definePreview({ decorators: [ (story) ({ components: { story }, template: div stylemargin: 3em;story //div, }), ], });2.5 Web ComponentsLitimport { html } from lit; export default { decorators: [(story) htmldiv stylemargin: 3em${story()}/div], };Web Components 渲染器使用lit的html标签模板story()以函数形式调用。TS 版本引入Preview类型并声明const preview: Preview {...}CSF Next 版本则使用definePreview。2.6 Solidexport default { decorators: [ (Story) ( div style{{ margin: 3em }} Story / /div ), ], };Solid 写法与 React 高度相似TS 版导入storybook-solidjs-vite的Preview类型。三、全局装饰器的底层执行机制3.1 合并composeConfigs 中的数组拼接所有 Story 的装饰器在项目注解project annotations合并阶段被统一收集。composeConfigs.ts 通过getArrayField(moduleExportList, decorators, { reverseFileOrder: ... })从各个配置模块中抽取decorators数组并默认按文件顺序反转合并以保证先加载的文件装饰器位于外层。这一行为可通过features.legacyDecoratorFileOrder特性开关回退到旧版顺序参见 main-config-features-legacy-decorator-file-order.md。3.2 组合defaultDecorateStory 的洋葱模型decorators.ts 中的defaultDecorateStory使用decorators.reduce(...)将所有装饰器逐层组合成一个洋葱式渲染链最外层装饰器先执行逐层向内最终由 Story 本身的渲染函数收尾。源码中bindWithContext将部分装饰后的 storyFn 绑定上下文并sanitizeStoryContextUpdate过滤掉componentId、title、id、parameters等只读静态键防止装饰器内部调用storyFn({ ... })时覆盖这些保留字段。3.3 归一化Story 级与组件级装饰器在 normalizeStory.ts 中单个 Story 的装饰器由storyObject.decorators组件级与story?.decoratorsStory 级拼接而成。结合全局装饰器一个 Story 最终命中的装饰器链为全局装饰器按定义顺序执行组件级装饰器按定义顺序执行Story 级装饰器按定义顺序执行由最内层向外。这与 docs/writing-stories/decorators.mdx 描述的继承规则完全一致。四、全局装饰器的进阶用法基于上下文的参数化装饰器函数的第二个参数是story context包含args、argTypes、globals、hooks、parameters、viewMode等属性。利用它可以让同一个全局装饰器根据 Story 元数据动态切换行为例如通过parameters.pageLayout决定是否应用页面级布局import { withLayout } from ./withLayout; export default { decorators: [withLayout], };其中withLayout读取context.parameters.pageLayout取值为page或page-mobile来决定包裹方式。完整示例见 decorator-parameterized-in-preview.md。同样的技术也用于配置 Mock Provider 以切换组件获得的主题参见 mocking-providers 配置章节。在 Vue 渲染器中若装饰器需要读取globals必须经由setup函数透传以保证响应式并可结合computed派生值示例见 decorator-with-reactive-globals.md调用 Storybook 的 API hooks如useArgs、useGlobals时同样应在装饰器中通过storybook/preview-api导入对应 hook 以避免重渲染错误参见 decorator-with-updateArgs.md。五、最佳实践与注意事项保持 Story 纯净装饰器之外的组件应保持为被测组件的纯粹渲染额外的 HTML 或包装组件只应出现在装饰器中。这样 Source Doc Block 等文档块才能正确提取源码。连接型组件的数据注入如果组件依赖外部加载的数据可以用全局装饰器以 Mock 方式提供数据无需把数据重构为 args具体策略可参考 building pages in Storybook。慎用全局作用域全局装饰器作用于所有 Story因此只放置真正普适的包装间距、主题 Provider、路由/Store Provider。针对单个组件或 Story 的定制优先使用低层级装饰器。顺序敏感由于洋葱模型的存在多个全局装饰器的定义顺序就是它们的执行顺序调整数组顺序即可改变包裹层次。结语全局装饰器是 Storybook 配置层最常用的扩展点之一。通过.storybook/preview中的decorators导出开发者可以用各框架惯用的语法为全部 Story 统一注入布局与上下文理解其背后的composeConfigs合并与defaultDecorateStory洋葱组合机制则能在多装饰器叠加、文件加载顺序等复杂场景下准确预判渲染结果。相关完整代码可继续阅读 preview-api 模块 及其测试 decorators.test.ts。【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价