用 MDX 与 Meta、Controls Doc Block 编写 Button 组件自定义文档:Storybook 基线示例逐行解读
【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook
在 Storybook 中,Meta与Controls是 Docs 体系里两个最基础的 Doc Block:前者决定自定义 MDX 文档挂接到哪个组件、放在侧边栏何处,后者则把组件的参数(args)渲染成一张可交互的属性表。本文以当前仓库docs/_snippets/storybook-auto-docs-baseline-example.md这份"基线示例"为骨架,逐行拆解它为Button组件编写自定义文档的标准写法,并延伸到 Meta Doc Block API 与 Controls Doc Block API,让你能直接复用这套模板为自己的组件写出"定义 / 用法 / 入参"结构清晰、且可交互的 MDX 文档页。
这份片段本身并非孤立文件,它是 MDX 编写自定义文档 中 "Using theMetaDoc Block" 一节的完整示例(由<CodeSnippets path="storybook-auto-docs-baseline-example.md" />注入),展示了撰写组件自定义 MDX 文档时的"标配骨架"。
基线示例在官方文档中的位置
storybook-auto-docs-baseline-example.md 提供两个几乎同构的Button.mdx版本,区别只在于Meta块的使用方式:
custom-title标签页:<Meta title="Button" />,用title指定文档在侧边栏的标题位置;of-prop标签页:<Meta of={ButtonStories} />,用of把这份 MDX 文档挂接到具体的 CSF 故事文件。
两者共用同一种正文组织方式:# Definition(组件是什么)、## Usage(怎么用、有哪些变体)、## Inputs(组件入参,通过<Controls />自动生成)。这说明 Storybook 官方推荐的"组件自定义文档"本质上是结构化 Markdown + 组件参数表格的组合。
完整的两种写法如下(与仓库片段逐字一致):
import { Meta, Controls } from '@storybook/addon-docs/blocks'; <Meta title="Button" /> # Definition Button is a clickable interactive element that triggers a response. You can place text and icons inside of a button. Buttons are often used for form submissions and to toggle elements into view. ## Usage The component comes in different variants such as `primary`, `secondary`, `large` and `small` which you can use to alter the look and feel of the button. ## Inputs Button has the following properties: <Controls />import { Meta, Controls } from '@storybook/addon-docs/blocks'; import * as ButtonStories from './Button.stories'; <Meta of={ButtonStories} /> # Definition Button is a clickable interactive element that triggers a response. You can place text and icons inside of a button. Buttons are often used for form submissions and to toggle elements into view. ## Usage The component comes in different variants such as `primary`, `secondary`, `large` and `small` which you can use to alter the look and feel of the button. ## Inputs Button has the following properties: <Controls />逐行拆解:一个 MDX 文档页由哪几部分组成
对照仓库中更完整的解释(见 MDX 编写自定义文档 的 "Anatomy of MDX" 一节),这份示例可以被切分为四层:
1. 从@storybook/addon-docs/blocks导入 Doc Block
import { Meta, Controls } from '@storybook/addon-docs/blocks';MDX 允许在一个文件里同时写 Markdown、JSX 与 story 引用。@storybook/addon-docs/blocks是 Doc Block(文档专用组件库)的统一导出入口,Meta与Controls均来自这里。它们的真实实现位于仓库 code/addons/docs/src/blocks/blocks/Meta.tsx 与 code/addons/docs/src/blocks/blocks/Controls.tsx,由 addon-docs 提供。
2. 用Meta块锚定文档
<Meta title="Button" />或
import * as ButtonStories from './Button.stories'; <Meta of={ButtonStories} />Meta块是两份版本差异的集中点,也是理解 MDX 文档归属机制的关键(详见下文专节)。注意:Meta不渲染任何可见内容,它只负责把这篇文档挂到组件、故事与侧边栏结构中。
3. 用 Markdown 标题组织叙述性内容
# Definition ## Usage ## Inputs示例依次回答了三个问题:组件是什么(Definition)、有哪些使用变体(Usage)、有哪些入参(Inputs)。MDX 默认支持 standard markdown(CommonMark),因此普通标题、段落、列表、表格都可直接使用;正文每一段落由空行分隔,切忌把不同语言块紧贴在一起,否则可能触发难以定位的解析错误。
4. 用Controls块插入可交互参数表
<Controls />Controls会把当前组件/故事的 args 渲染成一张动态表格:既能当作组件的接口文档(列出每个参数的名字、类型、默认值与描述),也允许读者直接修改参数并即时作用于页面中已渲染的 story(配合Story、Canvas块)。
两种Meta写法的语义差异:Attached 与 Unattached
Meta块支持of、title、name、isTemplate四个属性(详见 Meta Doc Block API),其中决定文档归属的是前两者:
| 属性 | 类型 | 作用 |
|---|---|---|
of | CSF 文件的全量导出 | 把 MDX 文档"挂接"到某个故事文件(attached),文档会显示在该组件的故事列表旁,并可使用Stories等需要在 attached 模式下的块 |
title | string | 为未挂接(unattached)的 MDX 文件设置标题,从而把文档放到侧边栏任意路径节点下 |
name | string | 修改 attached 文档条目的显示名(默认取docs.defaultName,即"Docs"),可为同一组件挂多个 MDX 并分别命名 |
isTemplate | boolean | 声明该 MDX 文件作为自动文档(autodocs)模板使用,不按普通条目被索引 |
of-prop版本就是标准的attached写法。官方文档在 MDX 编写自定义文档 中特别强调了一个高频坑:
当为
Meta提供of属性时,务必引用故事文件的全量导出(即import * as ButtonStories from './Button.stories'),而不是组件本身或默认导出,否则会引发生成文档的渲染问题。
也就是说of={ButtonStories}指向的是一个模块命名空间对象,而非ButtonStories.default指向的组件。attached 之后,这份 MDX 在侧边栏中紧挨着 Button 的故事列表出现。
custom-title版本则是unattached写法:文件通过title="Button"控制侧边栏位置,但未与任何 CSF 文件绑定。若连title都不提供、也没有其他内容块,Storybook 会把这种页面视为"仅文档"(documentation-only)页面,并在侧边栏以不同形态渲染(见 storybook-auto-docs-mdx-docs-docs-only-page.md)。更进一步,如果你完全省略Meta块,Storybook 会依据文件在磁盘上的物理位置,用与 CSF auto-title 相同的启发式规则推断标题并放到侧边栏对应位置,用它覆盖同名组件自动生成的 autodocs 页面——这种"按文件系统组织文档"的方式常用于独立页面或测试指南。
Controls块:组件入参表格的自动来源
示例第三部分的<Controls />之所以没有显式传of,是因为在 attached 场景下它会自动读取当前 MDX 文档所挂接 CSF 文件的上下文。Controls支持以下属性(详见 Controls Doc Block API):
| 属性 | 类型 | 默认值 | 作用 |
|---|---|---|---|
of | Story 导出或 CSF 文件导出 | — | 指定从哪个 story 取控件;传 CSF 文件导出时使用文件内第一个(primary)story |
include | string[] \| RegExp | parameters.docs.controls.include | 仅展示匹配的参数控件 |
exclude | string[] \| RegExp | parameters.docs.controls.exclude | 排除匹配的参数控件 |
sort | 'none' \| 'alpha' \| 'requiredFirst' | parameters.docs.controls.sort或'none' | 控件排序:none按处理顺序、alpha按名称字母序、requiredFirst在字母序基础上把必填项置顶 |
与多数 Doc Block 一样,Controls既能用 MDX 属性配置,也能用命名空间参数parameters.docs.controls配置(可在项目 / 组件 / 故事三个层级覆盖)。而真正决定表格中出现哪些行、显示成文本框还是下拉框的,是每个参数在 CSF 里声明的argTypes(含name、description、type、control、defaultValue等)。如果你需要的是不带交互控件、只展示参数元数据的静态表格,官方建议改用ArgTypes块(见 ArgTypes Doc Block API)。
还有一个已知边界值得留意:只有在未关闭 story 内联渲染(Story/Canvas的inline配置保持开启)的前提下,Controls才能产生真正可用的交互控件;文档页内控件与 story 的联动是否生效,取决于该配置。
让它真正可运行:配套的 CSF 故事文件
of-prop版本里被引用的./Button.stories需要按 Component Story Format 编写。作为配套示意(并非仓库中的真实文件),一个最小化的 CSF 3 故事文件大致如下:
import type { Meta, StoryObj } from '@storybook/react-vite'; import { Button } from './Button'; const meta = { title: 'Button', component: Button, // 通过 argTypes 描述参数,Controls 表格即据此渲染 argTypes: { variant: { control: 'select', options: ['primary', 'secondary'], }, size: { control: 'radio', options: ['small', 'large'], }, }, } satisfies Meta<typeof Button>; export default meta; type Story = StoryObj<typeof meta>; export const Primary: Story = { args: { variant: 'primary', size: 'large', label: 'Submit' }, };这样 MDX 中<Meta of={ButtonStories} />就能把文档挂接到上面定义好的 "Button" 故事节点,<Controls />则基于meta.argTypes与各 story 的args渲染出入参表格。MDX 内文档正文与实际 CSF 文件保持"一描述、一声明"的职责分离:CSF 负责精确定义组件各状态与类型安全(TS 下还有自动补全),MDX 负责撰写可读的结构化文档并自由编排 JSX。
与 Autodocs 的关系:自定义 MDX 如何覆盖自动文档
如果你在main.js|ts中通过tags: ['autodocs']开启了自动文档(autodocs),Storybook 会自动为每个组件生成一份文档页;而本文这套 MDX 写法属于另一条路径——为组件编写自定义MDX 文档。两条路径共享同一批 Doc Block 与侧边栏体系:autodocs 生成页、自定义 MDX 页以及仅文档页面统一显示为Docs条目。当你用文件系统方式(省略Meta)提供的自定义 MDX 与某组件同名同位置时,它会覆盖该组件的 autodocs 页面;官方建议此时把tags: ['autodocs']从组件故事上移除,避免冲突报错(见 Autodocs 编写自动文档)。
在项目中使用基线模板的操作步骤
把这份基线示例落地到自己的 Storybook 项目只需四步:
- 确认已安装并注册
@storybook/addon-docs(React 框架下通常随 docs 能力默认启用)。 - 在组件旁新建
Button.mdx,照抄上文任一版本作为起点:需要与已有故事联动、想复用Stories等块时选of版本;只想把文档放到指定侧边栏位置时选title版本。 - 用 Markdown 保持
# Definition/## Usage/## Inputs这样的信息架构,在末尾用<Controls />(或显式<Controls of={ButtonStories.Primary} />)嵌入参数表。 - 确保
.storybook/main.js|ts的stories配置能同时匹配*.mdx与*.stories.@(js|jsx|mjs|ts|tsx)文件(典型写法如../src/**/*.mdx、../src/**/*.stories.@(js|jsx|mjs|ts|tsx))。
更复杂的场景(一页文档覆盖多个组件、引入外部 Markdown 的Markdown块、文档块进阶组合等)可继续参考 MDX 编写自定义文档 与 Doc Blocks 文档写作。
【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考