资讯动态

Storybook Svelte 装饰器实战:向装饰器组件传递 Props 的完整指南

发布时间:2026/9/10 17:58:08 来源:尧图企业网站定制
Storybook Svelte 装饰器实战向装饰器组件传递 Props 的完整指南【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook本篇技术指南围绕 Storybook 官方文档 Decorators 中Svelte 装饰器返回带 props 组件对象这一核心技法展开。你将学会为什么 Svelte 渲染器需要装饰器外壳组件、如何用{ Component, props }对象形态基于 story 上下文动态定制装饰器行为、以及装饰器在 Storybook 中的三种作用域与执行顺序。读完即可在 Svelte/SvelteKit 项目中写出可参数化、可复用的高阶装饰器。一、为什么 Svelte 装饰器需要外壳组件装饰器Decorator是 Storybook 中包裹 story、为其附加额外渲染能力的机制许多插件通过装饰器增强 story 的渲染效果或收集渲染细节编写 story 时装饰器也常被用来为 story 包裹额外的标记markup或上下文context模拟decorators.mdx。对于 Svelte 渲染器情况尤为特殊。当组件需要容器harness才能以有意义的方式渲染时——例如组件贴边渲染、需要留白间距时你无法像 React/JSX 那样在函数体内直接写div标记。Svelte 的装饰器函数返回值有两种形态() MarginDecorator直接返回一个 Svelte 组件Storybook 会把它当作装饰组件渲染() ({ Component: MarginDecorator, props: {...} })返回一个对象显式指定要渲染的组件及其 props——这正是本指南的核心主题。这种对象形态的返回值定义在 Svelte 渲染器的类型系统中types.tsexport interface SvelteStoryResult Props extends Recordstring, any any, Exports extends Recordstring, any any, Bindings extends keyof Props | string, { Component?: ComponentProps, Exports, Bindings; props?: Props; decorator?: ComponentProps, Exports, Bindings; }从源码可以看到SvelteStoryResult同时承载了Component、props与decorator三个可选字段。返回{ Component, props }时装饰器组件本质上也是一个标准的 Svelte 组件实例props会被直接传入其$props()。二、基础形态返回组件 vs 返回带 props 的对象2.1 只有Component的基础写法Svelte CSF 形态 展示了最简用法!-- YourComponent.stories.svelte -- script module import { defineMeta } from storybook/addon-svelte-csf; import YourComponent from ./YourComponent.svelte; import MarginDecorator from ./MarginDecorator.svelte; const { Story } defineMeta({ component: YourComponent, decorators: [() MarginDecorator], }); /scriptMarginDecorator.svelte是一个接收childrensnippet 并施加外边距的普通组件margindecorator.mdscript let { children } $props(); /script div {render children()} /div style div { margin: 3em; } /style2.2 进阶{ Component, props }对象形态your-component-with-decorator-with-props.md 展示了核心技法——装饰器函数返回带props的对象// YourComponent.stories.js import YourComponent from ./YourComponent.svelte; import MarginDecorator from ./MarginDecorator.svelte; export default { component: YourComponent, decorators: [ (story, { parameters }) ({ Component: MarginDecorator, // 向 MarginDecorator 组件传递 props props: { size: parameters.smallMargin ? small : medium }, }), ], };这里的props将直接注入到装饰器组件的$props()中。TypeScript 版本通过satisfies Metatypeof YourComponent获得完整类型检查// YourComponent.stories.ts import type { Meta } from storybook/your-framework; // 替换为 svelte-vite 或 sveltekit import YourComponent from ./YourComponent.svelte; import MarginDecorator from ./MarginDecorator.svelte; const meta { component: YourComponent, decorators: [ (story, { parameters }) ({ Component: MarginDecorator, props: { size: parameters.smallMargin ? small : medium }, }), ], } satisfies Metatypeof YourComponent; export default meta;Svelte CSF 形态同样支持script module import { defineMeta } from storybook/addon-svelte-csf; import YourComponent from ./YourComponent.svelte; import MarginDecorator from ./MarginDecorator.svelte; const { Story } defineMeta({ component: YourComponent, decorators: [ (story, { parameters }) ({ Component: MarginDecorator, props: { size: parameters.smallMargin ? small : medium }, }) ], }); /script装饰器组件据此消费 props这里假定MarginDecorator增加了size属性script let { children, size medium } $props(); /script div stylemargin: {size small ? 1rem : 3em}; {render children()} /div三、底层原理prepareStory如何消化装饰器返回值理解对象形态之所以可行关键在于 Svelte 渲染器对装饰器返回值的归一化处理。在 decorators.ts 的prepareStory函数中if (!story || Object.keys(story).length 0) { // story 为空或空对象使用上下文中的 component preparedStory { Component: context.component }; } else if (story.Component) { // story 已经是以 { Component, props } 准备的形态原样保留 preparedStory story; } else { // 否则假定 story 本身就是一个 Svelte 组件 preparedStory { Component: story }; } if (innerStory) { // 用 DecoratorHandler 包裹innerStory 作为被装饰组件preparedStory 作为装饰组件 return { Component: DecoratorHandler, props: { ...innerStory, decorator: preparedStory, }, }; } // 链中最后一个 story 附上 argTypes 以便从 argTypes 生成事件 return { ...preparedStory, argTypes: context.argTypes };三个分支清晰对应三种用法装饰器返回值prepareStory处理适用场景() ({})或空用context.component兜底无装饰、只透传() ({ Component, props })原样保留story.Component分支需要给装饰组件传 props() MyComponent包装成{ Component: story }最简装饰组件因此返回{ Component, props }的对象是 Svelte 渲染器原生支持的一等公民形态props会在DecoratorHandler.svelte中与 story 内容一起渲染。整个装饰链通过decorateStorydecorators.ts逐层 reduce 包裹每一层 decorator 接收内层 story 渲染函数与上下文最终由最内层的prepareStory(context, storyFn(context))收尾。四、读取 story 上下文实现动态定制{ Component, props }形态的真正威力在于props的值可以完全由装饰器函数的第二个参数——story 上下文context——动态计算。文档decorators.mdx列出的上下文属性包括属性含义典型用途argsstory 参数在装饰器中消费部分 argsstory 实现中不再重复argTypesStorybook 的 argTypes 类型元数据精细化定制 argsglobalsStorybook 全局 globals配合工具栏toolbars在 UI 中切换取值hooksStorybook API hooksuseArgs、useGlobals等装饰器与 story 渲染函数中均可使用与框架 hooks 混用时改用storybook/preview-api的对应 hook 避免重渲染错误parametersstory 静态元数据控制 Storybook 功能与插件行为最常用于装饰器参数化viewMode当前活动窗口canvas、docs按视图模式差异化渲染4.1 用parameters参数化装饰器文档给出的经典案例是定义parameters.pageLayout page或page-mobile来可选地应用布局。对应 Svelte 的 preview 全局装饰器decorator-parameterized-in-preview.md// .storybook/preview.ts import type { Preview } from storybook/your-framework; // svelte-vite / sveltekit import PageLayout from ./PageLayout.svelte; const preview: Preview { decorators: [ (story, { parameters }) { const { pageLayout } parameters; return { Component: PageLayout, props: { layout: pageLayout || default, children: story, }, }; }, ], }; export default preview;配套的PageLayout.sveltedecorator-parameterized-in-preview.mdscript langts interface Props { layout?: page | page-mobile | default; children?: import(svelte).Snippet; } let { layout default, children }: Props $props(); /script !-- 你的页面布局实现可能比这复杂得多 -- div class{layout} {render children?.()} /div之后任意 story 只需声明export default { parameters: { pageLayout: page }, // 或 page-mobile };即可切换布局——这正是把parameters作为开关与props动态映射的标准模式与本节核心 snippet 中parameters.smallMargin的用法同源。4.2 结合 Svelte 的setContext提供上下文Svelte 装饰器还可以利用 story 上下文调用 Svelte 的 context APIdecorators.mdx。相关 snippet your-component-with-decorator-with-context.md 展示了从globals读取值并用setContext提供给组件// YourComponent.stories.js import { setContext } from svelte; import YourComponent from ./YourComponent.svelte; import MarginDecorator from ./MarginDecorator.svelte; export default { component: YourComponent, decorators: [ (story, { globals }) { const marginSize globals.marginSize small ? small : medium; setContext(marginSize, marginSize); return { Component: MarginDecorator }; }, ], };装饰器组件通过getContext消费script import { getContext } from svelte; let { children } $props(); const size getContext(marginSize) || medium; const margin size small ? 1rem : 3rem; /script div stylemargin: {margin}; {render children?.()} /div注意props显式传入与setContext隐式共享是 Svelte 装饰器的两种互补手段——前者适合当前这一层装饰器自己的可配置项后者适合向整棵装饰子树广播。五、装饰器的三种作用域与执行顺序文档decorators.mdx将装饰器分为三个层级Story 装饰器作用于单个 story。Svelte CSF 在Story组件上使用decorators属性CSF 命名导出则用decoratorskey示例见 button-story-decorator.md。文档特别建议保持 story 是被测组件的纯净渲染额外 HTML 或组件一律放进装饰器这样 Source Doc Block 的代码展示效果最佳。Component 装饰器作用于组件的全部 story。Svelte CSF 在defineMeta中加入decoratorsCSF 默认导出default export则使用decoratorskeybutton-story-component-decorator.md。本指南核心 snippet 中的decorators正属于这一层。Global 装饰器作用于所有 story定义在.storybook/preview.ts|tsx的decorators导出中storybook-preview-global-decorator.md。执行顺序story 渲染时全局装饰器按定义顺序 → 组件装饰器按定义顺序 → story 装饰器按定义顺序其中 story 装饰器从最内层开始、逐层向外执行并沿层级向上。这意味着全局装饰器永远是最外层包裹组件装饰器包裹在全局装饰器内部story 装饰器最贴近组件本身。若{ Component, props }形态的装饰器出现在多个层级props 计算会在各自层级各自执行形成由外向内逐层定制的效果。六、实战建议与注意事项props命名对齐$props()对象形态中props的 key 必须与装饰器组件声明的$props()属性完全一致否则 Svelte 运行时会告警例如size、layout、children。children由 Storybook 注入装饰器组件的childrensnippet 不需要也不应该由你在props中手动传入——它由DecoratorHandler注入被装饰的 story。你在props中只管自己的可配置项。类型安全TS 项目中用satisfies Metatypeof YourComponent或 Svelte CSF 的defineMeta即可让props获得推断与校验框架导入占位符storybook/your-framework需替换为实际包名storybook/svelte-vite或storybook/sveltekit。装饰器保持纯净装饰器只负责包装渲染业务数据模拟可移步 build-pages-with-storybook.mdx 章节用装饰器为已连接的组件提供 mock 数据从而避免重构组件去接收 args。避免重渲染陷阱在装饰器或 story 渲染函数中使用hooks如useArgs、useGlobals时若与框架 hooks如 React 的useState混用应改用storybook/preview-api提供的对应 hook。七、总结Svelte 渲染器把装饰器返回值统一归一化为{ Component, props }结构decorators.ts因此props是装饰器组件接收配置的一等途径。结合 story 上下文中的parameters、globals、args等字段你可以构建出完全参数化的布局装饰器、主题装饰器与数据 mock 装饰器——它们既能复用于整个项目全局/组件级也能精确作用于单个 story且与 Svelte 的setContext/getContext生态无缝协作。延伸阅读装饰器官方文档docs/writing-stories/decorators.mdx本文核心代码片段docs/_snippets/your-component-with-decorator-with-props.md装饰器组件示例docs/_snippets/margindecorator.md、docs/_snippets/your-component-with-decorator.md参数化全局装饰器docs/_snippets/decorator-parameterized-in-preview.mdcontext 装饰器示例docs/_snippets/your-component-with-decorator-with-context.mdSvelte 渲染器源码code/renderers/svelte/src/decorators.ts、code/renderers/svelte/src/types.ts相关能力argsargs.mdx、globals 与工具栏toolbars-and-globals.mdx、在 Storybook 中构建页面build-pages-with-storybook.mdx【免费下载链接】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 小时内与您沟通定制方案

免费获取报价