资讯动态

Storybook Vue3 装饰器进阶:用 Preview API 的 useArgs 与 updateArgs 让 Decorator 具备可交互状态

发布时间:2026/9/18 1:19:42 来源:尧图企业网站定制
Storybook Vue3 装饰器进阶用 Preview API 的 useArgs 与 updateArgs 让 Decorator 具备可交互状态在 Storybook 中Decorator装饰器通常被用来为 story 包裹额外渲染或模拟上下文而 Vue 渲染器的 Decorator 还有一层特殊要求globals、args 等数据必须经过 Vue 的setup函数显式返回才能在模板中保持完全响应式。本篇文章以仓库 decorators 官方文档 中的 “Preview API hooks” 一节及其配套代码片段 decorator-with-updateArgs.md 为主体讲解如何在 Vue3 Decorator 内部调用useArgs()/updateArgs()通过装饰器自带的按钮驱动 story 参数变化从而把“可交互的外部操作 UI”与“被测试组件本身”解耦。读完你可以在自己的.storybook/preview.js或preview.ts中复刻出可用的全局交互式装饰器并理解其背后的事件与重渲染机制。先理解Decorator 的第二个参数 Story Context在 Decorators 文档 中Decorator 函数签名的第二个参数是story context它向装饰器暴露了整条 story 的运行期上下文常见字段包括args当前 story 的参数你可以在装饰器中消费部分 args而不必在组件实现里重复声明argTypesStorybook 的 argTypes 配置globals作用于整个 Storybook 的全局参数配合 Toolbars 功能可在 UI 中直接切换hooksStorybook 的 API hooks如useArgs、useGlobals它们同时存在于装饰器与 story 渲染函数中。若在渲染函数里与框架 hooks如 React 的useState、useEffect混用应改用storybook/preview-api中导出的等价实现以避免重渲染时出错parametersstory 的静态元数据常用于控制 Storybook 功能与 addon 的行为viewMode当前 Storybook 激活的窗口如canvas、docs。从 Vue 的角度出发真正有用的模式是在 Decorator 里读取 story context再把需要的值通过setup()返回给模板这正是下面两个代码片段所演示的做法。Vue 装饰器的关键约束经 setup 返回才具备响应式同属 Vue 渲染器章节的兄弟片段 decorator-with-reactive-globals.md 明确指出To ensureglobalsin Vue decorators are fully reactive, you must pass them through thesetupfunction. You can also compute derived values with Vuescomputedfunction.这句话同样适用于任何要在 Vue 装饰器模板中被绑定、被事件读取的 Storybook 状态。因为 Vue3 的组合式渲染基于setup()的返回值建立模板作用域若状态只是被闭包引用而不经setup()暴露模板中的绑定不会得到响应式追踪。因此装饰器返回的“组件描述对象”必须包含components: { story }、setup()以及template这实质上是在装饰器内部声明了一个小型的 Vue 组件壳由它来包裹被装饰的 story。核心示例用 updateArgs 实现“自增按钮”装饰器decorator-with-updateArgs.md给出了完整可运行的两个版本.storybook/preview.js与.storybook/preview.ts这也是该片段被用于Vue 渲染器的“Preview API hooks”小节的原因——它让 Vue 装饰器具备了驱动 story args 的交互能力。JavaScript 版本.storybook/preview.jsimport { useArgs } from storybook/preview-api; const WithIncrementDecorator { args: { counter: 0, }, decorators: [ (story, { args }) { const [, updateArgs] useArgs(); return { components: { story }, setup() { return { args, updateArgs }; }, template: div button click() updateArgs({ counter: args.counter 1 }) Increment /button story / /div , }; }, ], };TypeScript 版本.storybook/preview.tsimport { useArgs } from storybook/preview-api; import type { Meta, StoryObj } from storybook/vue3; const WithIncrementDecorator: StoryObjMetatypeof MyComponent { args: { counter: 0, }, decorators: [ (story, { args }) { const [, updateArgs] useArgs(); return { components: { story }, setup() { return { args, updateArgs }; }, template: div button click() updateArgs({ counter: args.counter 1 }) Increment /button story / /div , }; }, ], };逐段拆解其含义useArgs()的解构写法Hook 返回三元组[args, updateArgs, resetArgs]这里只取updateArgs通过数组第一位的空位跳过args因为模板中真正读取的 args 来自 context 解构。useArgs需要消费一个能被storybook/vue3的Metatypeof MyComponent类型化的StoryObj对象——注意 TS 版本中MyComponent需要在你的 story 文件或模块中实际定义并被类型系统解析。在setup()中暴露args与updateArgs一并被setup()返回。这样模板中的{{ args.counter }}绑定与click事件处理器都处于 Vue 的响应式作用域内点击按钮后模板会自动更新同时story组件也因收到新的 args 而重渲染。组件壳封装components: { story }把原始 story 注册为壳组件的子组件story /的位置即原始 story 渲染的位置按钮 UI 完全位于组件壳即装饰器内故事本身依旧保持“纯粹渲染被测组件”的形态。args 合并语义updateArgs({ counter: args.counter 1 })传入的是部分参数对象底层对 story args 做的是局部更新而非整体替换因此即便 story 还有其他 args 也不会被抹掉。这段装饰器属于全局装饰器它被放在.storybook/preview.js/.storybook/preview.ts中TS 版以preview对象导出后作为export default preview提供给 Storybook 的 preview 配置因此默认作用于整个项目所有 story。如果只想作用于某个组件或某条 story可以把同样的decorators配置下放到对应 CSF 文件的meta默认导出或具名导出上机制与书写位置无关——详见 Decorators 文档。底层原理useArgs 如何把“点击”变成“重渲染”片段短小但它背后是完整的 Preview API 实现理解这条链路有助于避免误用。1. useArgs 只是通道事件的封装useArgs的实现位于 code/core/src/preview-api/modules/addons/hooks.tsexport function useArgsTArgs extends Args Args(): [ TArgs, (newArgs: PartialTArgs) void, (argNames?: (keyof TArgs)[]) void, ] { const channel addons.getChannel(); const { id: storyId, args } useStoryContextRenderer, TArgs(); const updateArgs useCallback( (updatedArgs: PartialTArgs) channel.emit(UPDATE_STORY_ARGS, { storyId, updatedArgs }), [channel, storyId] ); const resetArgs useCallback( (argNames?: (keyof TArgs)[]) channel.emit(RESET_STORY_ARGS, { storyId, argNames }), [channel, storyId] ); return [args as TArgs, updateArgs, resetArgs]; }关键信息有三点当前 args 来自useStoryContext()与渲染期的 story context 同源所以 context 解构出的args与 hook 返回的args指向同一份当前值updateArgs并不直接改状态而是把{ storyId, updatedArgs }通过 channel 发出UPDATE_STORY_ARGS事件——它是 Storybook preview 与 manager 之间共享的“参数输入流”事件该通道事件同时承载画布与 Controls 等 UI 的状态同步返回值中的第三个函数resetArgs支持传入 arg 名数组进行定向重置这在实现“恢复默认”类交互时非常实用。2. Preview 侧监听并驱动重渲染preview 端在 code/core/src/preview-api/modules/preview-web/Preview.tsx 注册监听this.channel.on(UPDATE_STORY_ARGS, this.onUpdateArgs.bind(this));随后由onUpdateArgs同文件约 L350 起根据storyId定位当前 story合并updatedArgs后触发 story 的重新渲染。这也解释了示例中“按钮每点击一次args.counter递增、故事随之刷新”的现象。3. Hook 驱动的循环渲染保护所有 Storybook preview hooks 都要经过 applyHooks 的应用器包装它会维护一个hooks.hasUpdates标志只要某次渲染期间有 hook 触发了状态更新就会立刻再次执行decorated(context)直到没有更多更新为止并用RENDER_LIMIT25 次封顶、抛出“Too many re-renders”错误来阻止死循环。换言之装饰器里updateArgs导致的连锁重渲染是被 Storybook 统一收敛并保护的不需要也不应该由业务代码自行处理循环。从源码结构看 useArgs 的三条使用纪律结合 hooks.ts 的实现与报错文案可以归纳出三条容易被忽略的边界只能在装饰器与 story 函数内调用。当在错误位置调用时getHooksContextOrThrow会抛出invalidHooksError其信息明示Storybook preview hooks can only be called inside decorators and story functions。请勿在 story 文件顶层、模块作用域或异步回调中调用。与框架 hooks 混用要用 preview-api 的等价物。报错文案给出明确指引在同一个渲染函数里同时使用 Storybook hooks如useArgs与框架 hooks如 Vue 的ref/computed之外、React 系的useState时使用storybook/preview-api提供的等价实现可以避免重渲染错位。Hook 的调用顺序与依赖数组需要保持稳定。useHook在 UPDATE 阶段会校验 hook 顺序、依赖数组长度与类型是否在两次渲染间发生变化否则会输出 warning。装饰器内若有多个useArgs/useGlobals请保证它们每次渲染都以相同的顺序与结构被调用。实战延伸什么时候该用这种装饰器把交互 UI 放进装饰器而不是写进 story最直接收益是让 story 保持纯粹——组件只负责渲染按钮、面板等“试验台”设施被隔离在壳层中这也与 Decorators 文档 中 “ensure that the story remains a pure rendering of the component under test” 的建议一致并能让 Source Doc Block 输出更干净的组件源码。同类可以自然扩展的玩法包括翻页 / 分步组件updateArgs({ page: args.page 1 })配合禁用上一页按钮填充表单或受控输入用装饰器生成调试输入框把值写回 args观察不同参数组合下的组件表现主题/加载状态切换读取useStoryContext()返回parameters配合updateGlobals做全局级切换对应 useGlobals需要“恢复初值”的交互解构出第三位的resetArgs并在按钮上调用。要注意的是这类装饰器驱动的 args 变更与用户在Controls 面板手动改参数走的是同一条 args 状态管线同一UPDATE_STORY_ARGS事件语义因此两者能够保持同步感知不会出现“面板里是一个值、按钮改完是另一个值”的分裂状态。小结decorator-with-updateArgs.md用 30 余行代码浓缩了 Vue3 装饰器中最有战斗力的一类用法setup()暴露 →useArgs()/updateArgs()驱动 → story 自动重渲染。把它放回 Decorators 文档 的上下文里看它是 “Preview API hooks” 与 “Context for mocking” 两节的落地示范而其运行机制则完整实现在 preview-api hooks 实现 与 preview-web 的事件监听 中。如果要在仓库内做进一步验证可以参考 hooks.test.ts 中针对UPDATE_STORY_ARGS触发的断言测试以及 PreviewWeb.test.ts 中大量围绕 story args 更新的通道事件测试用例——它们从测试层面再次印证了本文描述的数据流。创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

免费获取报价