Storybook 的 `previewHead` 与 `previewBody` 配置指南:以编程方式调整预览 HTML

发布时间:2026/9/8 22:30:35

Storybook 的 `previewHead` 与 `previewBody` 配置指南:以编程方式调整预览 HTML Storybook 的previewHead与previewBody配置指南以编程方式调整预览 HTML【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook本篇指南讲解 Storybook 中两个由 Preset 驱动的配置入口previewHead与previewBody。它们在.storybook/main.js或main.ts中通过函数接收 Storybook 渲染 iframe 当前的head、body内容并返回修改后的字符串可用于按环境条件注入脚本、样式或字体也能被 addon 作者用于封装 UI 定制能力。读完本文你将掌握两种配置的签名、适用场景、静态文件preview-head.html/preview-body.html与编程方案的取舍以及底层模板是如何与 HTML 文件合并的。配置项速览类型与文档位置previewHead与previewBody属于 main.js|ts 顶层配置 家族官方文档分别定义在main-config-preview-head.mdx类型为(head: string) string用于程序化调整预览区head。main-config-preview-body.mdx类型为(body: string) string用于程序化调整预览区body。在类型层面它们被统一声明在核心类型模块 code/core/src/types/modules/core-common.ts 中/** * Programmatically modify the preview head/body HTML. The previewHead and previewBody functions * accept a string, which is the existing head/body, and return a modified string. */ previewHead?: PresetValueStorybookConfigRaw[previewHead]; previewBody?: PresetValueStorybookConfigRaw[previewBody];注意这里的PresetValue语义这两个字段本质上是 Preset 入口preset 机制的一个组成部分。也就是说它们并不只是你项目的某次性配置而是 Storybook 在加载、合并 presets 时对每个 preset 的previewHead/previewBody依次调用后拼接得到最终 HTML 的预设值。正因如此writing-presets.mdx 明确指出预设 API 提供对 UI 配置的访问包括通过previewHead与previewBody配置的 preview 的head、bodyHTML 元素与使用preview-head.html、preview-body.html文件的效果类似。调用链路common-preset.ts在实现层面Storybook 的核心预设 code/core/src/core-server/presets/common-preset.ts 提供了两个核心 preset 函数export const previewHead async (base: any, { configDir, presets }: Options) { const interpolations await presets.applyRecordstring, string(env); return getPreviewHeadTemplate(configDir, interpolations); }; export const previewBody async (base: any, { configDir, presets }: Options) { const interpolations await presets.applyRecordstring, string(env); return getPreviewBodyTemplate(configDir, interpolations); };它们做的事是先通过presets.apply(env)收集环境变量插值再读取模板文件内容详见下文模板如何生成拿到已有字符串后交给你的previewHead(head ...)/previewBody(body ...)修改。换句话说你函数收到的字符串参数是底层模板已经渲染好的head/body内容你只需在其基础上追加或改写并原样返回。何时用编程式配置、何时用静态 HTML 文件Storybook 提供了两套等价机制向预览 iframe 注入内容需求场景静态 HTML 文件方案编程式 preset 方案注入固定脚本 / 样式无需条件判断在.storybook/preview-head.html或preview-body.html中添加内容无需使用previewHead/previewBody按环境、按特性开关条件注入无法实现HTML 文件是纯静态的previewHead/previewBody函数返回拼接结果开发插件 / addon 需要修改 UI用户难以复用 addon 的逻辑addon 在 preset 中导出这两个字段随 addon 一并生效官方文档在 main-config-preview-head.mdx 给出的 Callout 很明确如果你不需要程序化调整 preview 的head可以直接改用preview-head.html添加脚本与样式previewBody对应preview-body.html。反之需要条件判断或要在 addon 里封装能力时就使用previewHead/previewBody函数。这个设计在 template.ts 的实现中也得到印证模板函数会先检查.storybook/preview-head.html/preview-body.html是否存在存在则把静态文件内容与内置基础模板拼接再交给预设管线。也就是说HTML 文件方案与函数方案并不会冲突而是分层处理——文件内容先被合并进基础 HTML你的函数再拿到合并结果做最后一步加工。模板如何生成preview-head.html/preview-body.html与基础模板合并previewHead/previewBody的函数参数到底长什么样看 code/core/src/common/utils/template.ts 的实现export function getPreviewHeadTemplate(configDirPath, interpolations?) { const base readFileSync( join(resolvePackageDir(storybook), assets/server/base-preview-head.html), utf8 ); const headHtmlPath resolve(configDirPath, preview-head.html); let result base; if (existsSync(headHtmlPath)) { result readFileSync(headHtmlPath, utf8); } return interpolate(result, interpolations); }对应地getPreviewBodyTemplate的逻辑是将用户preview-body.html的内容前置到内置基础模板base-preview-body.html之前再合并。两处都会经过interpolate()做环境变量插值——即把%VAR_NAME%占位符替换为presets.apply(env)得到的环境变量值。配套单元测试 code/core/src/common/utils/tests/template.test.ts 也验证了两个关键行为当.storybook/preview-body.html不存在时返回空内容 / 仅基础模板当文件存在时返回用户文件内容 基础模板的合并结果。值得注意的实现细节head合并时静态内容被追加到基础模板之后result fileContent而body合并时静态内容被插入到基础模板之前fileContent result。这意味着你自定义的head内容会出现在 Storybook 内置 head 内容之后通常更适合做覆盖而自定义 body 内容会出现在基础 body 之前。在.storybook/main.ts中使用previewHead文档示例的主用法是按环境条件注入样式或脚本。以 CSF 3 与标准配置文件为例修改 代码示例片段 中Head (CSF 3)对应的 TS 写法previewHead版本其他渲染器与格式仅导入路径不同// .storybook/main.ts // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. import type { StorybookConfig } from storybook/your-framework; const config: StorybookConfig { previewHead: (head) ${head} style html, body { background: #827979; } /style , }; export default config;核心要点必须保留${head}收到的参数是当前已有head内容的完整字符串忘记拼接会直接破坏 Storybook 预览所需的既有脚本与样式。返回完整的新字符串你的函数签名是(head: string) string返回什么Storybook 就使用什么。JSCommonJS/ESM写法与之完全一致只是把类型注解去掉、直接导出对象// .storybook/main.js export default { previewHead: (head) ${head} style html, body { background: #827979; } /style , };在.storybook/main.ts中使用previewBodypreviewBody的签名是(body: string) string典型用法是在页面底部按条件追加第三方脚本例如分析脚本。文档中的 Body 示例CSF 3 的 TS 写法为// .storybook/main.ts // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. import type { StorybookConfig } from storybook/your-framework; const config: StorybookConfig { previewBody: (body) ${body} ${ process.env.ANALYTICS_ID ? script srchttps://cdn.example.com/analytics.js/script : } , }; export default config;这里展示了与previewHead不同的典型用途把注入逻辑建立在环境变量判断之上process.env.ANALYTICS_ID存在时才输出script标签因为返回的是普通字符串你可以做任意字符串处理如条件拼接、压缩空白或插值使用该功能时开发与生产构建环境下process.env的可用变量以 main-config-env 描述的加载机制为准。各框架 / 格式的代码形态CSF 3 与 CSF Next针对不同渲染器和新的配置写法官方代码片段 main-config-preview.md 覆盖了多个变体完整包含写法形态导入来源配置对象CSF 3.storybook/main.js直接导出{ previewHead, previewBody }普通对象字面量CSF 3.storybook/main.tsimport type { StorybookConfig } from storybook/your-framework类型为StorybookConfigCSF Next 实验性main.tsimport { defineMain } from storybook/your-framework/node用defineMain({ ... })包裹CSF Next 的实验形态示例以 Vue 渲染器为例// .storybook/main.ts import { defineMain } from storybook/vue3-vite/node; export default defineMain({ previewHead: (head) ${head} style html, body { background: #827979; } /style , });而 React 系列的 CSF Next 写法把类型导入从主包换到 Node 入口如storybook/react-vite/nodeAngular 为storybook/angular/nodeWeb Components 为storybook/web-components-vite/node。JS 文件同样支持defineMain只需去掉类型注解。需要区分文档中的previewHead/previewBody配置与另一组head/body静态文件方案并不冲突同时配置时按前面模板如何生成一节的顺序合并。此外向**管理界面manager**注入内容应使用 main-config-manager-head 对应的机制不要与这里的预览区配置混用。典型场景与最佳实践小结综合官方文档与源码可以总结以下使用建议addon 作者的首选封装官方在 main-config-preview-head.mdx 与 main-config-preview-body.mdx 中明确写Most often used by addon authors最常被插件作者使用。addon 在自身 preset 中导出previewHead/previewBody用户安装后即可自动获得注入效果无需手动编辑 HTML 文件。条件注入优先用函数从仓库的common-preset.ts看函数形式在整个 preset 管线中最后执行拿到的是合并后的完整内容因此可以基于process.env或自身逻辑决定保留、替换或追加。永远保留入参前缀两个函数都是接收完整字符串 → 返回完整字符串的纯函数式约定见 core-common.ts 的注释漏掉${head}/${body}会导致 Storybook 预览页面缺失运行时所需的基础 HTML。静态内容优先用文件若注入的是固定不变的脚本/样式直接用preview-head.html/preview-body.html更直观、无需经过函数层仓库模板函数template.ts会自动检测并合并这些文件。头部与 body 的合并顺序不同自定义 head 内容追加在基础 head 之后、自定义 body 内容前置在基础 body 之前涉及样式覆盖或首个脚本执行时机时可利用这一顺序差异。通过previewHead与previewBody你可以在不修改 Storybook 内部渲染模板的前提下对组件预览 iframe 的文档头部与正文做精确、可复用、可条件化的定制——无论是为项目引入全局主题样式、按环境加载分析脚本还是以 addon 形式向用户交付开箱即用的 UI 增强能力。【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/8 22:25:35

基于multisim的产品数字计件器电路设计

任务要求: 产品数字计件器是用于准确地完成工厂产品出厂传送带中的产品计件和统计显示。按下启动按钮,设备会自动扫描对产品数量进行计数、显示和存储,并显示计数结果。需要重置计数器时,按下清零按钮即可。 技术要求:…

2026/9/8 23:45:47

RPCS3使用教程:PS3模拟器从固件到手柄的完整指南

RPCS3使用教程:PS3模拟器从固件到手柄的完整指南 【免费下载链接】rpcs3 PlayStation 3 emulator and debugger 项目地址: https://gitcode.com/GitHub_Trending/rp/rpcs3 RPCS3是一款开源的PlayStation 3模拟器,它能在你的Windows、Linux或macOS…

2026/9/8 23:45:47

Atmosphere DNS重定向配置:三步悄悄屏蔽任天堂遥测服务器

Atmosphere DNS重定向配置:三步悄悄屏蔽任天堂遥测服务器 【免费下载链接】Atmosphere Atmosphre is a work-in-progress customized firmware for the Nintendo Switch. 项目地址: https://gitcode.com/GitHub_Trending/at/Atmosphere 每次开机联网&#xf…

2026/9/8 23:45:47

RPCS3 汉化补丁安装教程:3 步把 PS3 游戏界面变成中文

RPCS3 汉化补丁安装教程:3 步把 PS3 游戏界面变成中文 【免费下载链接】rpcs3 PlayStation 3 emulator and debugger 项目地址: https://gitcode.com/GitHub_Trending/rp/rpcs3 本文以 RPCS3 的内置 Patch Manager 为主线,讲清汉化补丁从拿到文件…

2026/9/8 7:15:10

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/8 7:15:10

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/8 0:01:49

踩多轮坑才跑通|OpenClaw 3.1.0 双平台本地 AI 自动化搭建实操实录

🔹 工具简述 OpenClaw 是一款备受开发者与办公人群青睐的开源本地智能工具,凭借离线本地运行、可视化图形面板、全流程自主任务处理三大核心特点,积累了众多忠实用户。与普通对话类 AI 产品不同,它能够直接调用电脑的软硬件操作权…

2026/9/8 0:01:50

拒绝复杂命令行,Hermes Agent 一键包快速解锁智能办公能力

🔍前言 不少想要体验 Hermes Agent 办公能力的使用者,往往会被复杂的环境配置拦住使用脚步。手动下载匹配依赖、反复调整系统目录、处理命令行持续报错、修复权限异常、补全丢失核心文件等一系列操作,对普通使用者而言门槛较高,很…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/7 22:45:59

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码