Storybook MDX 组件文档实战以 Checkbox 为例用 Meta 与 Canvas Doc Blocks 组合 CSF 故事本文围绕 Checkbox.mdx 完整示例片段 及其配套故事文件 Checkbox.stories 各框架写法系统拆解在 Storybook 中编写「组件即文档docs」页面的完整链路如何让 Markdown 正文、MetaDoc Block、CanvasDoc Block 与按 Component Story FormatCSF 定义的故事协同工作覆盖 React/Vue/Angular/Svelte/Web Components 等框架并兼容 CSF 3、CSF Next 与 Svelte CSF 多种编写范式。读完你将能独立照此模板为任意组件产出既规范又具备交互性的 MDX 文档页。该代码片段并非孤立存在而是官方 写作文档指南 的「基本示例」区与 命名组件与层级 中的核心教学素材因此在仓库内具有很高的权威性与可复用价值。示例全景三个文件如何组成一份组件文档示例围绕一个最常见的表单控件 Checkbox 展开完整链路由三部分拼装而成组件实现本身如Checkbox.tsx、Checkbox.svelte、Checkbox.vue、checkbox.component.ts等随框架而异故事文件Checkbox.stories.*以 CSF 导出组件实例状态如本文的Unchecked见 checkbox-story-csf.md文档文件Checkbox.mdx用 Markdown 写说明文字、用 JSX Doc Blocks 把故事嵌进文档页见 checkbox-story.md。三者通过「导入命名空间 of属性」绑定。这种拆分正是 MDX 文档 中反复强调的设计哲学CSF 擅长精炼地定义组件每个状态的示例且配合 TypeScript 可获类型安全与自动补全MDX 擅长撰写结构化文档并与交互式 JSX 组件自由组合。两份片段各自聚焦一个职责再经由 Doc Blocks 缝合。MDX 文档结构解剖checkbox-story.md 本身按渲染器renderer与制式提供了三种等价代码块正文内容完全一致差异仅在 import 目标变体tabTitle导入的故事文件通用框架common默认./Checkbox.storiesSvelteSvelte CSF./Checkbox.stories.svelteSvelteCSF 3 语法CSF 3./Checkbox.stories逐块拆解其结构import { Canvas, Meta } from storybook/addon-docs/blocks; import * as CheckboxStories from ./Checkbox.stories; Meta of{CheckboxStories} / # Checkbox A checkbox is a square box that can be activated or deactivated when ticked. Use checkboxes to select one or more options from a list of choices. Canvas of{CheckboxStories.Unchecked} /导入 Doc Blocks 与故事命名空间首行从storybook/addon-docs/blocks导入两个 Doc Block 组件Meta与Canvas。storybook/addon-docs即文档与 MDX 支持的内置机制在仓库 code/addons/docs 中实现/blocks是其导出的文档组件子入口。随后以命名空间导入方式引入同目录故事文件的所有导出import * as CheckboxStories from ./Checkbox.stories;必须使用命名空间导入* as而非默认导出导入因为后续Meta与Canvas需要引用该文件的「整组导出」含 meta 与各 story 对象。Meta把文档挂载到组件节点Meta of{CheckboxStories} /决定文档在侧边栏中的位置。此处of指向整份 CSF 文件的命名空间使文档紧邻 Checkbox 的 stories 节点展示而不是产生一个孤立页面。官方文档在 mdx.mdx 中给出了特别提醒Meta的of必须引用故事文件的完整导出集合而不是组件本身否则生成的文档可能出现渲染问题。Meta还支持两个常用属性调整文档呈现name自定义文档节点标题。默认标题为Docs例如Meta of{CheckboxStories} nameInfo /title把文档节点放到导航层级中的任意位置例如Meta titlepath/to/node /。Markdown 正文与 JSX 混排的硬性规则# Checkbox标题与两段说明文字是标准 CommonMark 语法MDX 天然支持还可通过插件扩展 GitHub Flavored MarkdownGFM。需要特别注意的是 MDX 的块级语法边界整个文件由「用空行分隔的多个块」组成Markdown 与 JSX 混排时必须用空行隔开不同块忘记加空行会引发有时难以排查的解析错误。这是 mdx.mdx 明确警告的易错点。Canvas把 CSF 故事渲染进文档最后一行Canvas of{CheckboxStories.Unchecked} /是最核心的展示逻辑Canvas会在文档中渲染一个带边框与工具条的画布把名为Unchecked的故事组件在未勾选状态下的真实渲染实时嵌入文档同时默认附带源码查看能力——读者可展开并查看该故事对应的代码。of属性接收具体的单个故事导出其 API 细节将在下文展开。故事从哪来Checkbox.stories 多框架写法全景MDX 文件里的Canvas of{CheckboxStories.Unchecked} /依赖故事文件必须导出一个Unchecked导出。checkbox-story-csf.md 给出了跨 Angular、Svelte、Web Components、Vue、Reactcommon等生态的等价实现是理解「同一语义、不同框架语法」的最佳对照表。CSF 3组件故事格式的标准现代写法对多数框架CSF 3 的核心是export default一份meta含component与若干命名导出的 story 对象。以通用 TypeScript 写法为例import type { Meta, StoryObj } from storybook/your-framework; import { Checkbox } from ./Checkbox; const meta { component: Checkbox, } satisfies Metatypeof Checkbox; export default meta; type Story StoryObjtypeof meta; export const Unchecked: Story { args: { label: Unchecked, }, };要点导入来源需按框架替换为实际包名如react-vite、nextjs、vue3-vite、angular、web-components-vite、svelte-vite、sveltekit等注释中明确提示了这一点// Replace your-framework with …用satisfies Metatypeof meta做类型收窄既可让 meta 对象字段获得完整校验又保留字面量类型以推断 Story 的args类型故事通过export const Unchecked: Story { args: {...} }定义args会被 Storybook 注入组件并映射到 Addons如 Controls 面板从而在画布上直接看到label被传入后的效果。label: Unchecked这种以字符串为值的写法即是 Args 机制的直观体现关于 args 的深入用法见 Writing Stories - Args。未使用 TS 的通用 JS 版本去掉类型标注后同样清晰export default { component: Checkbox }加export const Unchecked { args: {...} }。Angular 版本的差异在于组件类型来自storybook/angular且导入的是装饰器风格组件类import type { Meta, StoryObj } from storybook/angular; import { Checkbox } from ./checkbox.component; const meta: MetaCheckbox { component: Checkbox, }; export default meta; type Story StoryObjCheckbox; export const Unchecked: Story { args: { label: Unchecked }, };Web Components 则因组件即自定义标签component需写成字符串形式的标签名export default { component: demo-checkbox, }; export const Unchecked { args: { label: Unchecked }, };CSF Next实验性preview.meta / preview.story 链式 APIcheckbox-story-csf.md 中反复出现一种带 实验标记的变体即 CSF Next 写法例如 React/TSimport preview from ../.storybook/preview; import { Checkbox } from ./Checkbox; const meta preview.meta({ component: Checkbox, }); export const Unchecked meta.story({ args: { label: Unchecked, }, });其与 CSF 3 的核心差异是不再写export default meta 独立具名导出而是从.storybook/preview导出单例preview通过preview.meta({...})创建带类型上下文的 meta再用meta.story({...})创建故事。它把「定义」收敛为链式调用类型可从 preview 中继承组件自动挂载到项目级配置。示例同时保留了 JS 版本以便在过渡期同时提供两种制式片段内有注释JS snippets still needed while providing both CSF 3 Next说明原因。Svelte CSFdefineMeta Story 组件的声明式写法对 Svelte 生态仓库采用storybook/addon-svelte-csf源码位于 code/addons 下的 Svelte CSF 工具链 配套目录提供专属语法。Svelte CSF 版故事文件不再是.stories.js|ts而是组件化的Checkbox.stories.sveltescript module import { defineMeta } from storybook/addon-svelte-csf; import Checkbox from ./Checkbox.svelte; const { Story } defineMeta({ component: Checkbox, }); /script Story nameUnchecked args{{ label: Unchecked, }} /defineMeta在script module模块级中执行并返回Story组件每个Story nameUnchecked args{{...}} /声明一条故事。也正因如此checkbox-story.md 中 Svelte 变体的 import 目标写作./Checkbox.stories.svelte而其他框架统一为./Checkbox.stories。同样场景下若 Svelte 项目偏好标准 CSF 3也提供等价 JS/TS 版export default { component: Checkbox }只是导入路径指向 Svelte 组件文件。这种「同一组件、双语法并存」的安排在真实仓库的 Svelte 相关测试工程如 test-storybooks/portable-stories-kitchen-sink中亦有印证。用 title 组织层级从设计系统视角看 Checkbox如果说上面解决的是「文档怎么把故事画出来」那么 checkbox-story-grouped.md 解决的是「这组故事出现在侧边栏的哪个位置」。为将 Checkbox 归类到设计系统的基础原子组件示例在 meta 中加入titleconst meta: MetaCheckbox { title: Design System/Atoms/Checkbox, component: Checkbox, };title使用/作为分隔符生成多级目录树Category根分类Design System侧边栏顶层的根节点Folder文件夹Atoms用于把同类原子组件聚合为可展开分组Component组件Checkbox故事的载体。片段内部注释亦指出title属性是可选的——Storybook 支持依据故事文件的物理路径自动推导标题即 auto-title。显式title适合需要跨文件物理位置组织或接入既有设计系统命名的场景若不写则依赖 sidebar-and-urls 中的 CSF 3 Auto Titles 机制 隐式推导。这正是 命名组件与层级 中「显式」与「隐式」两种组织结构方式的具体体现。Canvas 与 Meta 的进阶参数把示例用得更专业文档示例中的Canvas与Meta都只是最简用法Doc Block Canvas API 揭示了其完整可配置面ofStory export指定在画布中渲染哪条故事。本文示例即Canvas of{CheckboxStories.Unchecked} /metaCSF 文件导出渲染「未通过Meta关联到本文档」的其他 CSF 文件中的故事传入完整的导出集合而非默认导出。典型场景如文档主写 Button 却需临时展示 Header 的LoggedIn故事layoutcentered | fullscreen | padded默认padded控制画布内故事的摆放方式默认值依次回退到parameters.layout/parameters.docs.canvas.layoutsourceStatehidden | shown | none控制源码面板初始状态additionalActions在画布右下角追加自定义按钮如「在 GitHub 打开」className提供自定义样式的挂载点。此外Canvas的相关 prop 大多能以parameters.docs.canvas.*命名空间形式在全局/组件/故事层级统一预设实现配置与 JSX 的解耦。Meta的name/title/of语义与参数化预设逻辑同源参考 Meta Doc Block API。文档片段在仓库中的组织方式与延伸阅读值得说明的是checkbox-story*.md并非可独立运行的文档页而是官方文档站点的可复用代码片段素材它们被以CodeSnippets pathcheckbox-story.md /的方式注入 docs/writing-docs/mdx.mdx 的基本示例与 docs/writing-stories/naming-components-and-hierarchy.mdx 的分组示例中配合renderer、tabTitle、language等元信息实现「同一示例、多框架标签页切换」的渲染能力。跟随这份模板落地到自己的组件库你只需写好组件与其 CSF 故事文件注意导出Unchecked之类的具名故事新建同名.mdx导入Meta/Canvas并以* as引入故事文件用Meta of{Stories} /定位文档、#标题书写说明、Canvas of{Stories.X} /嵌入每个需要展示的故事依据设计系统用/分隔的title组织侧边栏层级。如需更进一步可继续研读同目录的 Doc Blocks 家族、Args 机制 与 Parameters 配置理解每个 block prop 背后的参数化默认来源把组件文档打磨到自动化交付水准。创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考