ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Storybook 根级 Preset(Root Preset)Addon 编写指南:零配置注册 previewAnnotations 与 managerEntries

Storybook 根级 Preset(Root Preset)Addon 编写指南:零配置注册 previewAnnotations 与 managerEntries Storybook 根级 PresetRoot PresetAddon 编写指南零配置注册 previewAnnotations 与 managerEntries【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook根级 Presetroot-level preset是 Storybook 预设体系中面向最终用户的一层负责在用户不做任何额外配置的前提下完成 addon 的注册与装载把预览端能力如参数、装饰器与界面端能力如面板、工具栏工具分别通过previewAnnotations与managerEntries暴露给 Storybook。本文以仓库中的示例代码为骨架结合 Storybook 源码中managerEntries与previewAnnotations的真实消费链路讲解这类 Preset 的写法、构建与发布约定以及使用中的注意事项。背景Preset 是什么根级 Preset 处在哪一层按官方文档 writing-presets.mdx 的定义Storybook presets 是一组预先配置好的设置或配置让开发者能够通过一组 API 快速组合功能、集成第三方插件并定制 Storybook 的行为。开发一个 preset addon 时通常把它拆成两类文件各司其职本地 PresetLocal preset面向构建过程负责封装与 addon 相关的构建配置例如对 Webpack/Vite 两个 builder 的适配、Babel 处理或第三方集成。这类 Preset 一般写作项目的preset.js内部函数并导给构建器使用。根级 PresetRoot-level preset面向最终用户user-facing其职责是无需用户做任何额外配置就把 addon 注册好。它把所有与预览渲染story 渲染相关的功能例如 parameters通过previewAnnotations一并打包给预览端把所有与 UI 相关的功能例如 addon 面板、工具条通过managerEntries打包给管理器端。下面的小节来自 docs/_snippets/storybook-addons-root-preset.md就是一个最小根级 Preset 的完整形态本文其余部分将围绕它逐行展开export const previewAnnotations [import.meta.resolve(./dist/preview)]; export const managerEntries [import.meta.resolve(./dist/manager)]; export * from ./dist/preset.js;拆解最小根级 Preset三个导出各做什么一个根级 Preset 本质上就是一个 JS 模块Storybook 在读取 addon 时会自动识别并应用它的具名导出。上面这段代码只包含三块内容1.previewAnnotations把预览端代码接入渲染管线export const previewAnnotations [import.meta.resolve(./dist/preview)];previewAnnotations接收一个路径数组或一个返回路径数组的函数用于向预览端追加注解annotation模块。预览端是真正渲染 story、运行 decorators 和 parameters 的运行时环境。如果你希望 addon 给所有 story 提供默认参数、全局装饰器、全局类型或渲染前的初始化逻辑就把实现写在dist/preview这个模块里并在这里登记路径。这里的关键点是import.meta.resolve(./dist/preview)它以当前 Preset 文件为基准解析出编译产物的绝对文件路径而不是写死某个相对路径。因为你的包被安装到用户项目的node_modules之后Preset 模块本身的位置决定了./dist/preview指向何处import.meta.resolve能保证解析结果在发布后依然正确。引用的目标应当是编译后的产物本例为dist/preview即你的 addon 以 TypeScript 源码维护、以dist/形式发布的常规包结构。2.managerEntries把界面端入口接入 Manager 构建export const managerEntries [import.meta.resolve(./dist/manager)];managerEntries接收一个字符串数组用于注册**管理器端manager**的入口模块。管理器是 Storybook 的界面外壳搜索、导航、工具栏以及各类 addon 面板都渲染在 manager 里。文档 writing-presets.mdx 特别指出当你在编写一个会去加载自己无法控制的第三方 addon、但又需要访问特定功能或配置的 preset 时就需要通过managerEntries把这些 addon 的入口显式接进来。需要理解的是managerEntries描述的是你要打包进 manager 的入口文件而不是 addon 的功能本身。它与你 addon 包里用于向 manager 注册 UI 扩展tool、panel、tab 等的模块是两回事manager 端具体注册什么由dist/manager模块内部的实现决定。3.export * from ./dist/preset.js把其余 Preset 能力透传出去export * from ./dist/preset.js;根级 Preset 是用户接触 addon 的总入口所以它不仅要导出上面两个字段还要把编译产物里其他所有 Preset API例如babelDefault、webpackFinal、viteFinal、previewHead、previewBody、managerHead、entries、features等整体再导出一次。这样用户只要引用你的 addon 名就能一次性获得全部预设能力同时你的包代码仍然可以在src/preset.ts中按模块拆分维护编译成dist/preset.js后由这个薄薄的preset.js透传。仓库中的正式 addon 普遍采用源码拆分 顶层薄转发的结构。例如 code/addons/a11y/preset.js、code/addons/docs/preset.js、code/addons/vitest/preset.js 等文件正文都只有一行export * from ./dist/preset.js;也就是说各 addon 真正的 preset 逻辑可能包含previewAnnotations、managerEntries以及文档列出的其他 API全部由src/preset.ts编译到dist/preset.js再在包入口统一转发。这与示例代码的第三行完全一致可以把它视为 Storybook addon 生态约定俗成的包布局。previewAnnotations在源码中如何被消费弄清 Storybook 内部如何使用previewAnnotations有助于写出正确的 addon。从源码看它通过预设系统在各构建器中被读取并与用户自己的.storybook/preview文件合并在 Vite 构建器里code/builders/builder-vite/src/codegen-project-annotations.ts 调用presets.applyPreviewAnnotation[](previewAnnotations)取到所有预设贡献的注解再执行previewAnnotations: [...previewAnnotations, previewOrConfigFile]把预设注解与用户预览文件首尾相接后统一交给产物生成。Webpack 构建器同样如此code/builders/builder-webpack5/src/preview/virtual-module-mapping.ts 对presets.applyPreviewAnnotation[](previewAnnotations, [], options)的结果逐项转换为虚拟模块构造出预览端的入口映射。Vite 的依赖优化插件 code/builders/builder-vite/src/plugins/storybook-optimize-deps-plugin.ts 也会读取同样的previewAnnotations保证预设注入的模块被正确预打包。也就是说你在previewAnnotations里声明的每个路径最终都会以额外的 preview 模块身份被追加进预览运行时。因此示例中把./dist/preview放进去等价于在所有 story 渲染前额外加载我这份 preview 代码这与在用户侧书写.storybook/preview.js效果类似但由 addon 自动完成、对用户透明。仓库中还提供了函数形态的实现范例code/addons/mcp/src/preset.ts 导出如下export const previewAnnotations: PresetPropertyFnpreviewAnnotations async ( existingAnnotations [] ) { return [...existingAnnotations, path.join(import.meta.dirname, preview.js)]; };可以观察到两个实用细节其一previewAnnotations也支持异步函数接收已存在的注解数组作为入参并返回新数组方便与其他预设/用户配置合并其二追加路径同样基于模块自身位置import.meta.dirname计算得到与示例代码import.meta.resolve(./dist/preview)的思路完全一致。managerEntries在源码中如何被消费managerEntries的管理器侧装配发生在 Manager 构建阶段。在 code/core/src/builder-manager/index.ts 中可以看到 Manager 构建器取配置的方式const [managerEntriesFromPresets, envs] await Promise.all([ options.presets.apply(managerEntries, []), options.presets.applyRecordstring, string(env), ]);随后它把managerEntriesFromPresets作为 manager 打包的入口点与用户配置目录中的./manager入口合并后交给以 esbuild 为内核的构建流程打包成 IIFE 产物outdir指向./sb-addons、format: iife详见 code/core/src/builder-manager/index.ts。在类型层面Storybook 为这个配置字段给出的定义是managerEntries?: string[]见 code/core/src/types/modules/core-common.ts。从这段代码还能得到一个对 addon 作者重要的结论manager 的打包与预览端无关而是独立的小型 esbuild 构建。这也解释了为什么在 writing-presets.mdx 的故障排查章节中明确提示由于 Storybook 依赖 esbuild 而非 Webpack 构建 UImanager依赖旧managerWebpackAPI 的 preset 已不再生效应将其移除并把需要加载的额外文件转换为 JavaScript。什么时候用根级 Preset什么时候用addonsAPI对最终用户而言在.storybook/main.js|ts中通过 addons 配置 直接列出一串 addon 名称是更简单的接入方式——Storybook 会自动识别值是 preset 还是 addon 并完成加载。而根级 Preset 解决的是addon 开发与分发侧的问题当你发布一个 addon希望用户仅需在addons数组里填上包名、无需手工配置任何 preview/manager 细节即可获得完整能力时就在包里提供preset.js并导出上述三个字段。可以理解为面向 addon消费者推荐使用addonsAPI用户侧配置面向 addon作者通过根级 Preset 的previewAnnotations与managerEntries提供开箱即用的默认接线开发者侧实现。文档 writing-presets.mdx 还给出了同族的其他可组合能力当需要在用户配置之前提供 Babel 配置时可考虑babelDefault仅对内部使用 Babel 的框架生效SWC、esbuild 等编译器下会被忽略构建器相关使用viteFinal/webpackFinal需要在预览 HTML 的head/body注入内容时可使用previewHead/previewBody等价于用户侧的preview-head.html/preview-body.html修改管理器界面可使用managerHead还可以通过entries注册 preview 入口点例如做一个自动加载全部*.stories.js的 configure-storybook preset。这些 API 均可经由根级 Preset 文件统一转发导出。文件组织与构建约定小结综合文档与仓库实践一个标准的根级 Preset addon 推荐采用如下布局example-addon/ ├── src/ │ ├── preset.ts # 编写 previewAnnotations/managerEntries 及其余 preset API │ ├── preview.ts # 预览端逻辑装饰器、参数、全局类型…… │ └── manager.ts # 管理器端入口第三方 addon、UI 注册等 ├── preset.js # 包入口声明并转发即本文示例文件 └── package.json # exports 中将 preset 指向 ./preset.jspreset.ts编译后对应示例中的dist/preset.jspreview.ts、manager.ts分别编译为dist/preview、dist/manager。三个导出分别承担登记预览端注解、登记管理器端入口、透传其余 preset API。只要保证import.meta.resolve/import.meta.dirname指向的是发布后真实存在的编译产物用户在.storybook/main.js|ts的addons中填入包名即可完成零配置接入。若想继续深入建议依次阅读 writing-presets.mdxPreset 概念全貌、main-config-preview-annotations配置项语义、addon-types.mdxaddon 类型划分以及docs/addons下的其他 addon 开发指南代码侧则可对照 code/addons/mcp/src/preset.ts、code/core/src/builder-manager/index.ts 与 code/builders/builder-vite/src/codegen-project-annotations.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),仅供参考
返回列表