拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Storybook 插件内消费与更新 Globals:useGlobals、updateGlobals 与 FORCE_RE_RENDER 实战

Storybook 插件内消费与更新 GlobalsuseGlobals、updateGlobals 与 FORCE_RE_RENDER 实战【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook本指南基于 Storybook 官方文档中“在 Addon 内部更新 Globals 并刷新界面”的权威示例代码片段 docs/_snippets/addon-consume-and-update-globaltype.md由 docs/essentials/toolbars-and-globals.mdx 在 Updating globals from within an addon 一节引入并结合 Storybook 仓库的 manager-api、core-events 与 preview-api 源码进行纵深讲解。读完你将掌握在自研 Storybook 工具栏型 Addon 中如何用useGlobals()读取全局值、用updateGlobals()修改全局值并通过FORCE_RE_RENDER事件强制触发 story 重新渲染最终写出可复用的交互式 Toolbar 按钮。一、场景背景为什么 Addon 需要“读”和“改” Globals在 Storybook 中Globals 表示“全局的”非某个 story 专属渲染输入例如主题light/dark、语言 locale、背景色等。它不同于args——不会作为 story 参数传入而是在装饰器decorator与 story contextcontext.globals中被消费作用于所有 story。当 Globals 改变时story 会随之重新渲染、装饰器也会以新值重跑。官方推荐的使用路径有两种在.storybook/preview.*中通过globalTypestoolbar注解声明工具条用户在 UI 下拉菜单里改变全局值在 Addon面板或工具栏内部以代码方式读写 Globals——这是很多增强型插件主题切换、无障碍模拟、伪状态注入等的常见需求。本指南的关联片段正是第 2 条路径的官方最小示范一个工具栏按钮点击后在“开/关”两种全局状态间切换同时强制刷新当前渲染的 story。二、完整示例在 Addon 中读取并更新 Globals关联文档 docs/_snippets/addon-consume-and-update-globaltype.md 给出了一个完整的 React 工具栏组件。以下代码为原文完整复刻保存于你的 Addon 注册文件中例如your-addon-register-file.jsimport React, { useCallback } from react; import { OutlineIcon } from storybook/icons; import { useGlobals } from storybook/manager-api; import { addons } from storybook/preview-api; import { ToggleButton } from storybook/internal/components; import { FORCE_RE_RENDER } from storybook/internal/core-events; const ExampleToolbar () { const [globals, updateGlobals] useGlobals(); const isActive globals[my-param-key] || false; // Function that will update the global value and trigger a UI refresh. const refreshAndUpdateGlobal () { // Updates Storybook global value updateGlobals({ [my-param-key]: !isActive, }); // Invokes Storybooks addon API method (with the FORCE_RE_RENDER) event to trigger a UI refresh addons.getChannel().emit(FORCE_RE_RENDER); }; const toggleOutline useCallback(() refreshAndUpdateGlobal(), [isActive]); return ( ToggleButton keyExample paddingsmall variantghost pressed{isActive} onClick{toggleOutline} ariaLabelAddon feature tooltipToggle addon feature OutlineIcon / /ToggleButton ); };2.1 逐段拆解四类导入各司其职导入来源模块作用React, { useCallback }react编写 manager 侧 React 组件Toolbar 即 manager UIOutlineIconstorybook/icons按钮图标FAQ 中列出了所有可用于 toolbar/addon 的图标名useGlobalsstorybook/manager-api读取当前 Globals并拿到更新函数updateGlobalsaddonsstorybook/preview-api拿到 Addon 通信 channel用于发射事件ToggleButtonstorybook/internal/componentsStorybook 内部按钮组件支持pressed/padding/variant/tooltip等属性FORCE_RE_RENDERstorybook/internal/core-events事件常量值即字符串forceReRender用于强制刷新界面2.2 读取const [globals, updateGlobals] useGlobals()useGlobals()是从storybook/manager-api导出的 React Hook。在 manager 侧实现位于 code/core/src/manager-api/root.tsx#L515-L523export function useGlobals(): [ globals: Globals, updateGlobals: (newGlobals: Globals) void, storyGlobals: Globals, userGlobals: Globals, ] { const api useStorybookApi(); return [api.getGlobals(), api.updateGlobals, api.getStoryGlobals(), api.getUserGlobals()]; }注意返回值是四元组第一个元素是当前全局值对象第二个是更新函数第三、四个分别是 story 级与用户级 Globals。官方示例中只解构前两者即可满足“读全局、改全局”的需求。globals[my-param-key]就是读取当前开关值配合|| false做布尔兜底const isActive globals[my-param-key] || false;2.3 更新updateGlobals({ [my-param-key]: !isActive })更新函数接收一个局部 globals 对象Storybook 会把其中携带的键合并进当前全局状态。key 写成计算属性[my-param-key]是为了容纳任意自定义键名例如带-或命名字典key时其效果等同于{ my-param-key: !isActive }。点击后按钮将在两种状态之间反复切换。作为佐证仓库内真实 Addon 全部遵循这一模式例如 code/addons/a11y/src/components/VisionSimulator.tsx#L54-L62 用updateGlobals({ [VISION_GLOBAL_KEY]: selected })同步视觉模拟选项code/addons/pseudo-states/src/manager/PseudoStateTool.tsx#L30 用updateGlobals({ [PARAM_KEY]: {} })重置伪状态code/addons/themes/src/theme-switcher.tsx#L83-L104 用updateGlobals({ theme: alternateTheme })切换主题。2.4 刷新addons.getChannel().emit(FORCE_RE_RENDER)FORCE_RE_RENDER是 Storybook 内置核心事件之一定义于 code/core/src/core-events/index.ts#L21FORCE_RE_RENDER forceReRender。官方注释将其语义概括为 “re-render unchanged”见 code/core/src/preview-api/README-preview-web.md#L47。addons.getChannel()返回 Storybook 的通信 channelmanager 与预览 iframe 之间的事件总线.emit(FORCE_RE_RENDER)表示“请用当前状态原样重新渲染当前 story”。预览端在 code/core/src/preview-api/modules/preview-web/Preview.tsx#L144-L153 的setupListeners()中注册了对该事件的监听setupListeners() { this.channel.on(STORY_INDEX_INVALIDATED, this.onStoryIndexChanged.bind(this)); this.channel.on(UPDATE_GLOBALS, this.onUpdateGlobals.bind(this)); // ... this.channel.on(FORCE_RE_RENDER, this.onForceReRender.bind(this)); this.channel.on(FORCE_REMOUNT, this.onForceRemount.bind(this)); // ... }也就是说Addon 在 manager 侧updateGlobals修改全局值后再通过 channel 发射FORCE_RE_RENDER预览端收到事件即触发当前 story 以最新 Globals 重新渲染——这正是注释中所说的“trigger a UI refresh”。在 Storybook 内部机制中preview hooks 在“非渲染阶段”触发更新时同样会走这条通道见 code/core/src/preview-api/modules/addons/hooks.ts#L371-L383 的triggerUpdate()addons.getChannel().emit(FORCE_RE_RENDER)相关行为在 code/core/src/preview-api/modules/store/hooks.test.ts#L463-L524 中有专门测试断言expect(mockChannel.emit).toHaveBeenCalledWith(FORCE_RE_RENDER)。2.5 UIToggleButton与事件绑定const toggleOutline useCallback(() refreshAndUpdateGlobal(), [isActive]);用useCallback把回调的依赖收敛为isActive保证isActive变化后闭包中读到的是最新值ToggleButton上pressed{isActive}让按钮呈现“按下/激活”态onClick触发切换ariaLabel与tooltip分别用于无障碍与悬停提示。三、从“代码片段”到“可运行 Toolbar”完整接线3.1 第一步在 preview 中声明 globalTypes 与 initialGlobalsToolbar 消费的全局键需要先在.storybook/preview.*声明。参考官方片段 docs/_snippets/storybook-preview-configure-globaltypes.mdTS/React 版import type { Preview } from storybook/react-vite; const preview: Preview { globalTypes: { theme: { description: Global theme for components, toolbar: { title: Theme, // Toolbar 项的显示名 icon: circlehollow, // 未选中时显示的图标 items: [light, dark], dynamicTitle: true, // 根据当前选中值动态更新标题 }, }, }, initialGlobals: { theme: light, }, }; export default preview;需要强调的限制官方明确提示Globals 是“全局”的因此globalTypes与initialGlobals只能写在.storybook/preview.*即 docs/configure/index.mdx 的 “Configure story rendering” 所描述的项目级 preview 配置不能在单个 story/meta 中声明。toolbar.items支持两种形态纯字符串值数组或MenuItem对象数组。MenuItem 各字段见下表摘自关联主文档MenuItem类型说明是否必填valueString设置到 globals 中的菜单值是titleString菜单项的主文本是rightString显示在菜单右侧的文本否iconString该项被选中时 Toolbar 显示的图标否图标须取自storybook/icons中可用的图标名列表。3.2 第二步注册 Addon把ExampleToolbar导出后通过 manager 入口Addon 包中的manager.*或preset注册到 Storybook并在.storybook/main.ts的addons数组中加入你的 Addon// .storybook/main.ts const config { // ... addons: [your-addon-package-name], }; export default config;启动 Storybook 后Toolbar 中即可出现带OutlineIcon的切换按钮点击一次 →updateGlobals置为true并强制重渲染再点一次 → 恢复为false。若已有装饰器读取该 global例如基于context.globals[my-param-key]开关大纲/网格story 视觉会随之实时变化。消费 Globals 的装饰器写法可参考 docs/_snippets/storybook-preview-use-global-type.md包含 ReactThemeProvider、Vue Vuetify、Angular、Web Components 等框架示例。四、延伸在面板 Addon 中“只读” GlobalsuseGlobals 的另一半如果你做的是面板型PanelAddon只想展示当前全局值而无需修改官方配套片段 docs/_snippets/addon-consume-globaltype.md 展示了“读取并渲染主题对象”的用法。其核心只有一行const [{ theme: themeName }] useGlobals();随后将themeName映射为完整主题对象并用Source/Placeholder等组件在面板中渲染。这与工具栏示例形成互补读取用useGlobals()[0]修改用useGlobals()[1]即updateGlobals。两段片段在官方文档中的位置docs/essentials/toolbars-and-globals.mdxConsuming globals from within an addon →addon-consume-globaltype.mdUpdating globals from within an addon →addon-consume-and-update-globaltype.md本文主体。五、store 侧视角preview hooks 中的另一套 useGlobals需要区分的是storybook/manager-api的useGlobals面向managerAddon UI而 preview 渲染侧还存在一套面向 story/decorator 的 hooks 实现位于 code/core/src/preview-api/modules/addons/hooks.ts#L649被 story 内部用于订阅/更新全局值并驱动重渲染。若你需要在story 内部按单 story 读取 Locale 之类的 global而不用装饰器官方片段 docs/_snippets/my-component-story-use-globaltype.md 展示了从context.globals解构的方式。两者适用层级不同勿混用。六、仓库内真实 Addon 佐证这一“读 写 刷新”模式的工程可信度可从本仓库内置 Addon 中直接验证code/addons/pseudo-states/src/manager/PseudoStateTool.tsxconst [globals, updateGlobals] useGlobals()再通过Select的onReset/onChange调updateGlobals写入PARAM_KEYcode/addons/a11y/src/components/VisionSimulator.tsx#L54-L62同样useGlobals()updateGlobals同步无障碍视觉模拟全局值code/addons/themes/src/theme-switcher.tsx#L83-L104updateGlobals({ theme: alternateTheme })切换主题。这些官方向导 Addon 把文档示例固化成了线上代码可作为你实现自研 Addon 的参考蓝本。七、自查清单与关键结论实现“在 Addon 中消费并更新 Globals”牢记以下要点读取const [globals, updateGlobals] useGlobals()来自storybook/manager-api返回值实为四元组globals、updateGlobals、storyGlobals、userGlobals依据 code/core/src/manager-api/root.tsx#L515-L523。写入updateGlobals({ key: value })按键合并写入本身会经 Storybook 全局状态流程同步到 story。强制刷新 UIaddons.getChannel().emit(FORCE_RE_RENDER)FORCE_RE_RENDER常量值forceReRendercode/core/src/core-events/index.ts#L21预览端在setupListeners()中监听code/core/src/preview-api/modules/preview-web/Preview.tsx#L150。声明前置对应的globalTypes/initialGlobals只能在.storybook/preview.*配置story 级globals注解用于“锁定”某个 story 的取值但会禁用该 global 的 Toolbar 交互官方建议克制使用。UI 状态一致ToggleButton的pressed与useCallback的依赖数组必须与实际全局值同步避免闭包读到过期状态。借助 manager-api、preview-api、core-events 与内置 Addon 源码你可以把上面几十行示例扩展为具备完整状态读写与实时刷新能力的企业级 Storybook 插件功能。【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门