Storybook Addon 开发:使用setQueryParams删除 URL 查询参数
【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook
导读
本文聚焦 Storybook Addon API 中 URL 查询参数的生命周期管理:当你的自定义 Addon 通过setQueryParams把临时状态写入浏览器地址栏后,如何在需要时把它“擦除”。删除查询参数的正确姿势并不是从对象中剔除键,而是将该键的值显式设为null。本文将以 Storybook Addons API 文档 为依据,结合storybook/manager-api的真实实现源码,讲清楚“传null即删除”背后的机制,并给出可直接落地的示例代码。
为什么需要清除查询参数:查询参数是 Addon 的“临时存储”
在 docs/addons/addons-api.mdx 的api.setQueryParams()一节中,Storybook 官方给出明确定位:
This method allows you to set query string parameters. You can use that as temporary storage for addons.
即setQueryParams允许 Addon 把需要跨刷新、跨 story 切换保留的轻量状态(如面板开关、过滤词、上次选中的视图)写进浏览器 URL 的查询字符串中,充当一种无后端、可分享、可刷新恢复的临时存储。这也是 Addon 开发者接入 manager 端 UI 状态时最常用的方式之一。
调用addons.register()注册 Addon 时,回调函数会拿到 Storybook API 实例,所有 URL 相关操作都基于这个实例完成:
import { addons } from 'storybook/manager-api'; addons.register('my-organisation/my-addon', (api) => { // api.setQueryParams / api.getQueryParam / api.getUrlState ... });写入:把状态塞进 URL
在清除之前,先看如何写入。使用setQueryParams传入一个键值对象即可:
addons.register('my-organisation/my-addon', (api) => { api.setQueryParams({ exampleParameter: 'Sets the example parameter value', anotherParameter: 'Sets the another parameter value', }); });调用后,Storybook 会把这些键值同步到 manager 的地址栏查询字符串中,例如 URL 中会出现?...&exampleParameter=...,并且不会覆盖已经存在于地址栏中的其他参数(如args、globals)。
从源码类型定义看,setQueryParams接受的是这样一个宽松的对象类型,位于 code/core/src/manager-api/modules/url.ts:
interface QueryParamInput { [key: string]: string | undefined | null; }注意这里值只允许string | undefined | null,意味着查询参数的值只能是字符串——如果你要保存布尔值或对象,需要自行做序列化(例如JSON.stringify或String(bool))。
清除(本文核心):把值设为null而不是删除键
当某个参数不再需要时,文档在setQueryParams一节的末尾给出了明确的指导(docs/addons/addons-api.mdx):
Additionally, if you need to remove a query parameter, set it as
nullinstead of removing them from the addon.
对应的代码片段即 docs/_snippets/storybook-addons-api-disablequeryparams.md:
addons.register('my-organisation/my-addon', (api) => { api.setQueryParams({ exampleParameter: null, }); });这段代码执行后,exampleParameter会从 URL 中彻底消失。几个关键点:
- 不要在传入的临时对象上把键“去掉”后调用
setQueryParams,因为setQueryParams的语义是合并(merge)更新:只处理你传入的键,其它已存在的自定义参数保持不变。想删除某个参数,就必须显式地把它的值传成null; - 传
null之后,该键会从内部状态中被delete,同时地址栏中对应的key=value片段也会被清除; - 若 Addon 里通过
getQueryParam再次读取该键,会得到undefined,而不是残留的旧值。
源码级原理:null/undefined触发删除
setQueryParams的底层实现位于 code/core/src/manager-api/modules/url.ts,核心逻辑相当直白:
setQueryParams(input) { const { customQueryParams } = store.getState(); const update: QueryParams = { ...customQueryParams }; for (const [key, value] of Object.entries(input)) { if (value === null || value === undefined) { delete update[key]; } else { update[key] = value; } } if (!deepEqual(customQueryParams, update)) { store.setState({ customQueryParams: update }); provider.channel?.emit(UPDATE_QUERY_PARAMS, update); } },逐行解读这段实现,可以验证文档描述的准确性:
- 基于当前快照合并:先从 manager store 中读取已有的
customQueryParams,并浅拷贝出update,因此未在input中出现的旧参数不会被触碰; - 值判空即删除:遍历传入对象的每个键,一旦发现值为
null或undefined,就从update中用delete删除该键;否则写入新值——这正是“设null等于删除”的直接依据; - 变更保护:通过
deepEqual比较新旧状态,仅当确实发生变化时才触发store.setState与provider.channel?.emit(UPDATE_QUERY_PARAMS, update)。也就是说,对一个已经不存在的键反复传null是幂等的,不会产生多余的 store 更新和事件广播。
与之配套的getQueryParam(key)同样读取这份customQueryParams状态(见 code/core/src/manager-api/modules/url.ts):
getQueryParam(key) { const { customQueryParams } = store.getState(); return customQueryParams ? customQueryParams[key] : undefined; }getUrlState()则一次性返回path、hash、queryParams、storyId、viewMode、url等完整地址栏状态(见 code/core/src/manager-api/modules/url.ts),适合需要同时读取多个参数或在导航前后做判断的场景:
addons.register('my-organisation/my-addon', (api) => { const href = api.getUrlState({ selectedKind: 'kind', selectedStory: 'story', }).url; });值得补充的是,Storybook 自身也是通过setQueryParams维护args、globals等参数到地址栏的同步的。例如在 url.ts 的 updateArgsParam 中,当 story 切换或 args 更新时会执行api.setQueryParams({ args: argsString || null })——参数不再需要时同样使用null占位来清除。这说明“置空即清除”是 Storybook 内部统一遵循的约定,Addon 开发者照此办理即可与内置机制保持一致。
读取与验证:清除后如何确认
清除完成后,验证手段有两种:
- 直接读取:调用
api.getQueryParam('exampleParameter'),返回值应为undefined:
addons.register('my-organisation/my-addon', (api) => { api.getQueryParam('exampleParameter'); // undefined,说明参数已被移除 });- 观察地址栏:浏览器地址栏中对应
key=value片段会同步消失。由于参数是被写入 URL 的,用户手动刷新页面、或通过分享链接把 URL 发给他人后,被清除的参数不会再次“复活”。
实战组合:状态写入与清除的完整闭环
以一个“面板开关”型 Addon 为例,把以上 API 串联起来:注册面板组件时写入参数、面板激活状态变化时清除参数,从而保证 Addon 状态可被 URL 记住、也可被 URL 忘记。
import { addons } from 'storybook/manager-api'; addons.register('my-organisation/my-addon', (api) => { // 用户开启某个视图时写入标记 const enableHighlight = () => { api.setQueryParams({ highlight: 'on' }); }; // 用户关闭视图时清除标记:传 null,而非删键 const disableHighlight = () => { api.setQueryParams({ highlight: null }); }; // 初始化时恢复:读取不到即 undefined const current = api.getQueryParam('highlight'); // 'on' | undefined // 供你自己的 UI 组件调用,或绑定到 button 的 onClick api.addNotification({ id: 'highlight', content: { headline: 'Highlight Addon' } }); });在此基础上,Addon 的面板内部还可以通过useStorybookApiHook 拿到同样的api实例做状态联动(参见 imports 示例):
import { useStorybookApi } from 'storybook/manager-api'; function HighlightPanel() { const api = useStorybookApi(); const enabled = api.getQueryParam('highlight') !== undefined; return ( <button onClick={() => api.setQueryParams({ highlight: enabled ? null : 'on' })}> {enabled ? 'Disable highlight' : 'Enable highlight'} </button> ); }常见误区与注意事项
- 用“删键”代替“置空”不会生效:由于
setQueryParams按传入键做增量合并,键不在输入对象中就意味着“本次不改动”,因此删除必须显式传null; - 值类型限制:
QueryParamInput只接受字符串值,null/undefined专用于删除。对象、数字、布尔值等需先序列化为字符串再写入; - 只影响 manager 端 URL:
setQueryParams操作的是 Storybook manager 界面(addon 面板所在环境)的 URL 状态;若 Addon 需要往预览 iframe 传递数据,应走 channel 或预览参数等其它机制; - 幂等安全:
setQueryParams内置deepEqual变更检测(见 url.ts),重复清除不存在的键不会产生额外 store 更新,可在事件回调中放心反复调用; - 刷新与分享语义:写入的查询参数会真实反映在地址栏,因此请勿把敏感或超长数据(如完整日志)塞入 URL,它本质上是可公开浏览的“临时存储”。
小结
本文系统梳理了 Storybook Addon 通过 URL 查询参数管理临时状态的完整链路。核心结论可以归纳为一条规则:需要删除参数时,用setQueryParams({ key: null }),而不是把键从对象中剔除。这一约定不仅记录在 Addons API 文档 的setQueryParams章节中,也由 url.ts 的合并逻辑(value === null || value === undefined时执行delete update[key])提供了源码级的确认。配合getQueryParam读取与getUrlState快照,开发者可以轻松实现 Addon 状态“可写入、可读取、可清除”的完整闭环。
【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考