首页
/ Storybook Docs for Angular 实战指南:Compodoc Props 表、MDX 长文档与 IFrame 高度配置

Storybook Docs for Angular 实战指南:Compodoc Props 表、MDX 长文档与 IFrame 高度配置

2026-09-06 16:25:49作者:董宙帆

本文基于 Storybook 仓库中的官方框架文档 ANGULAR.md 整理,围绕 Angular 渲染器下 Docs 附加组件的完整配置链路展开:从安装 @storybook/addon-docs、自动生成 DocsPage,到借助 Compodoc 输出 documentation.json 生成 Props 表、用 MDX 编写长文档,再到 iframeHeightinline 参数的精细调优。读完你可以为 Angular 项目建立一套可运行的组件文档方案,并能从源码层面理解 Compodoc 元数据是如何被提取、解析并映射为 Controls/Docs 的 ArgTypes 的。

适用说明:这份文档描述的配置形态

原文档开头有一段版本说明:该页面记录的是在 5.3.0 中引入的一套 Storybook 配置方式,如需迁移到更新的配置格式,应查阅仓库根目录的 MIGRATION.md。因此下文的示例(storiesOf 风格参数、MDX 中 <Story name=...>props 写法、basic.parameters 挂载方式)都以该文档自身的写法为准,属于 Angular 框架下的传统文档配置形态;文末会补充当前仓库源码中该机制的演进情况,方便对照最新实现。

Docs 在 Angular 下的核心能力:

  • DocsPage:把 stories 自动转换为组件文档页面,展示在 Storybook UI 的 Docs 标签页;
  • MDX:以 Markdown 为基编写长文档,并内联嵌入 stories、Props 表等文档组件;
  • Props 表:依赖 Compodoc 生成的 API 元数据,支持 inputsoutputspropertiesmethodsview/content child/children 作为一等属性类型。

安装

第一步安装 @storybook/addon-docs,并保证与项目中其他 @storybook/* 包版本一致:

yarn add -D @storybook/addon-docs

然后在 .storybook/main.jsaddons 中注册:

export default {
  addons: ['@storybook/addon-docs'],
};

安装完成后,所有 stories 会自动获得基础的 DocsPage 文档,无需额外配置即可在 Storybook UI 的 Docs 标签页查看。

Props 表:基于 Compodoc 的 API 文档生成

Angular 的 Props 表是 Docs 中最需要额外配置的部分。其原理是:Compodoc 先静态解析项目源码,把组件、指令、管道等的输入/输出、属性、方法等信息序列化到 documentation.json;Storybook 的 Angular 渲染器再读取这份 JSON,将其中与当前 story 的 component 匹配的条目解析为 ArgTypes,驱动 Props 表与 Controls 面板。

方式一:sb init 自动配置

在执行 sb init 初始化 Storybook 时,CLI 会询问是否要为项目配置 Compodoc。回答 Yes 即可,初始化流程会自动完成依赖安装与 builder 配置,之后 Compodoc 直接可用。

仓库中对应的交互与执行逻辑在 Angular 框架包的 builder 中:sb init 后的 storybook / build-storybook target 会携带 compodoc 开关与 compodocArgs 参数。自动补齐默认参数的核心实现见 run-compodoc.ts

export const runCompodoc = async (
  { compodocArgs, tsconfig }: { compodocArgs: string[]; tsconfig: string },
  context: BuilderContext
): Promise<void> => {
  const tsConfigPath = toRelativePath(tsconfig);
  const finalCompodocArgs = [
    'compodoc',
    ...(hasTsConfigArg(compodocArgs) ? [] : ['-p', tsConfigPath]),
    ...(hasOutputArg(compodocArgs) ? [] : ['-d', `${context.workspaceRoot || '.'}`]),
    ...compodocArgs,
  ];
  // ...通过包管理器执行 `compodoc -p <tsconfig> -d <输出目录>`
};

两个细节值得注意:

  • compodocArgs 中未显式提供 -p(tsconfig 路径),会自动补上;未提供 -d/--output 时,输出目录默认是工作区根目录;
  • toRelativePath 会把绝对路径转为相对路径,这是为了规避 Compodoc 在 Windows 上处理绝对路径的已知问题。

builder 侧的触发点在 start-storybook/index.ts:当 target 配置中 compodoc: true 时,调用 runCompodoc,且 --quiet 时会给 Compodoc 追加 --silent 参数。

方式二:手动配置 Compodoc

1. 在 .storybook/preview.ts 中注册 documentation.json

import { setCompodocJson } from '@storybook/addon-docs/angular';
import docJson from '../documentation.json';

setCompodocJson(docJson);

2. 安装 Compodoc:

yarn add -D @compodoc/compodoc

3. 配置 Compodoc 生成 documentation.jsonangular.jsonprojects.<project>.architect 下,为 storybookbuild-storybook 两个 target 各添加 compodoc: truecompodocArgs,这样每次运行 Storybook 时都会在项目根目录(".")重新生成 documentation.json

// angular.json
{
  "projects": {
    "your-project": {
      "architect": {
        "storybook": {
          "...": "...",
          "compodoc": true,
          "compodocArgs": [
            "-e",      // 输出格式
            "json",
            "-d",      // 输出目录
            "."         // 项目根目录
          ]
        },
        "build-storybook": {
          "...": "...",
          "compodoc": true,
          "compodocArgs": [
            "-e",
            "json",
            "-d",
            "."
          ]
        }
      }
    }
  }
}

4. 在 story 元数据中声明 component 字段,Props 表靠它把 Compodoc 条目与组件关联起来:

import { AppComponent } from './app.component';

export default {
  title: 'App Component',
  component: AppComponent,
};

已知限制:按文档说明,此时还无法在编辑组件时动态刷新这份 JSON——需要重新运行 Storybook 重新生成。这一点从源码结构看也成立:Compodoc 的执行发生在 Angular CLI builder 启动 Storybook 的构建阶段(runCompodoc 只在 target 启动时跑一次),而非预览进程内的热更新链路。

源码纵深:setCompodocJson 与 ArgTypes 提取

setCompodocJson 的完整实现非常短,见 code/addons/docs/src/angular/index.ts

export const setCompodocJson = (compodocJson: any) => {
  // 开启 experimentalDocgenServer 时,Storybook 在服务端提取 Angular 元数据,
  // 既不读取这里的值也不读取 Compodoc 输出,此时会告警提示可以删除该调用
  if (globalThis.FEATURES?.experimentalDocgenServer) {
    logger.warn(
      'setCompodocJson() had no effect: ... You can delete the setCompodocJson call ...'
    );
    return;
  }

  (globalThis as any).__STORYBOOK_COMPODOC_JSON__ = compodocJson;
};

这揭示了该机制的演进脉络,可以推断出当前仓库中的三条事实:

  1. 默认链路setCompodocJson 把 JSON 挂到 globalThis.__STORYBOOK_COMPODOC_JSON__,预览侧从该全局变量读取——@storybook/angular@storybook/angular-vite 的浏览器适配层(如 code/frameworks/angular/src/client/compodoc.ts)都保留了这一历史读取路径;
  2. 共享解析层:Compodoc JSON 的解析与浏览器适配已抽取到独立包 @storybook/angular-compodoc,由 @storybook/angular@storybook/angular-vite 及其 Node docgen worker 共用,类型定义统一来自该包;
  3. 服务端 docgen 特性:启用 experimentalDocgenServer 特性后,Angular 元数据改为在服务端直接提取,完全绕开 Compodoc,这也是上文告警的由来。

Props 表展示的分组顺序与字段来源在 extract-arg-types.ts 中定义:

const SECTION_ORDER = [
  'properties',
  'inputs',
  'outputs',
  'methods',
  'view child',
  'view children',
  'content child',
  'content children',
];

这与原文档“支持 inputsoutputspropertiesmethodsview/content child/children 作为一等属性类型”的表述一一对应。该模块还提供 checkValidCompodocJsonfindComponentByNameextractArgTypesextractComponentDescription 等工具函数(通过 browser.ts 适配层 暴露),对应测试见 browser.test.tsextract-arg-types.test.ts

MDX 长文档

MDX 是另一种组织文档的方式:以 Markdown 写说明文字,同时内联嵌入 story、Props 表等文档组件。

前置依赖:Docs 对 react 有 peer dependency(用于渲染 MDX),若要用 MDX 写 stories,可能需要显式安装:

yarn add -D react

配置 stories 通配符:更新 .storybook/main.js,确保加载 MDX 文件:

export default {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
};

基础 MDX 示例

import { Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { AppComponent } from './app.component';

<Meta title='App Component' component={AppComponent} />

# App Component

Some **markdown** description, or whatever you want.

<Story name='basic' height='400px'>{{
  component: AppComponent,
  props: {},
}}</Story>

## ArgsTable

<ArgsTable of={AppComponent} />

原文档特意指出:component 需要同时写在 <Meta><Story> 中,当时这是冗余的(官方在 issue 中记录了后续改进计划)。另外注意:要让 Props 文档块生效,必须先按上文完成 Compodoc 配置。

用 MDX 表达 template / moduleMetadata / addDecorators 组合的 story:如果你原来在 storiesOf 里使用过这些 API,可以直接平移到 MDX:

import { Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { CheckboxComponent, RadioButtonComponent } from './my-components';
import { moduleMetadata } from '@storybook/angular';

<Meta title='Checkbox' decorators={[
  moduleMetadata({
    declarations: [CheckboxComponent]
  })
]} />

# Basic Checkbox

<Story name='basic check' height='400px'>{{
  template: `
    <div class="some-wrapper-with-padding">
      <my-checkbox [checked]="checked">Some Checkbox</my-checkbox>
    </div>
  `,
  props: {
    checked: true
  }
}}</Story>

# Basic Radiobutton

<Story name='basic radio' height='400px'>{{
  moduleMetadata: {
    declarations: [RadioButtonComponent]
  }
  template: `
    <div class="some-wrapper-with-padding">
      <my-radio-btn [checked]="checked">Some Checkbox</my-radio-btn>
    </div>
  `,
  props: {
    checked: true
  }
}}</Story>

可以看到 moduleMetadata 既可作为 Metadecorators(对整个文档页生效),也可直接写在单个 story 的 props 对象里(仅对该 story 生效)。

IFrame 高度

Storybook Docs 将所有 Angular story 渲染在 IFrame 中,默认高度为 60px。这个默认值可以全局修改,也可以逐 story 修改(DocsPage 与 MDX 两种方式都支持):

全局默认值,修改 .storybook/preview.ts

export const parameters = { docs: { story: { iframeHeight: '400px' } } };

DocsPage 中逐 story 修改,在 story 上本地设置参数:

export const basic = () => ...
basic.parameters = {
  docs: { story: { iframeHeight: '400px' } },
}

MDX 中逐 story 修改,直接使用 Story 元素的 height 属性:

<Story name='basic' height='400px'>{...}</Story>

Inline Stories

Angular 的 story 在 Docs 中默认以内联(inline)方式渲染。如果需要把 story 放进 IFrame(默认高度 100px,同样受 docs.story.iframeHeight 控制),使用 docs.story.inline 参数即可。对全部 story 生效时,更新 .storybook/preview.js

export const parameters = { docs: { story: { inline: false } } };

也就是说,Angular 渲染器在 Docs 里的展示策略是:inline: true(默认)→ 直接内联渲染;inline: false → 降级为带默认 100px 高度的 IFrame,高度再按 iframeHeight 参数覆盖。

相关仓库资源

小结

Angular 下的 Storybook Docs 遵循“Docs 附加组件 + 框架元数据源”的组合:@storybook/addon-docs 负责文档页渲染(DocsPage 与 MDX),而 Props 表与 Controls 的 API 元数据默认由 Compodoc 的 documentation.json 提供——sb init 自动配置或手动在 angular.json 中开启 compodoc target 选项二选一,再通过 setCompodocJson 把 JSON 交给预览端。iframeHeight(默认 60px)与 inline(默认内联)两个参数则覆盖了绝大多数展示层调优需求。理解了这套链路后,无论仓库后续将元数据提取迁移到服务端 docgen(experimentalDocgenServer)还是统一到 @storybook/angular-compodoc 共享包,配置层的心智模型都保持不变。

登录后查看全文
热门项目推荐
相关项目推荐