Storybook Docs for Angular 实战指南:Compodoc Props 表、MDX 长文档与 IFrame 高度配置
本文基于 Storybook 仓库中的官方框架文档 ANGULAR.md 整理,围绕 Angular 渲染器下 Docs 附加组件的完整配置链路展开:从安装 @storybook/addon-docs、自动生成 DocsPage,到借助 Compodoc 输出 documentation.json 生成 Props 表、用 MDX 编写长文档,再到 iframeHeight 与 inline 参数的精细调优。读完你可以为 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 元数据,支持
inputs、outputs、properties、methods、view/content child/children作为一等属性类型。
安装
第一步安装 @storybook/addon-docs,并保证与项目中其他 @storybook/* 包版本一致:
yarn add -D @storybook/addon-docs
然后在 .storybook/main.js 的 addons 中注册:
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.json。 在 angular.json 的 projects.<project>.architect 下,为 storybook 与 build-storybook 两个 target 各添加 compodoc: true 和 compodocArgs,这样每次运行 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;
};
这揭示了该机制的演进脉络,可以推断出当前仓库中的三条事实:
- 默认链路:
setCompodocJson把 JSON 挂到globalThis.__STORYBOOK_COMPODOC_JSON__,预览侧从该全局变量读取——@storybook/angular与@storybook/angular-vite的浏览器适配层(如 code/frameworks/angular/src/client/compodoc.ts)都保留了这一历史读取路径; - 共享解析层:Compodoc JSON 的解析与浏览器适配已抽取到独立包
@storybook/angular-compodoc,由@storybook/angular、@storybook/angular-vite及其 Node docgen worker 共用,类型定义统一来自该包; - 服务端 docgen 特性:启用
experimentalDocgenServer特性后,Angular 元数据改为在服务端直接提取,完全绕开 Compodoc,这也是上文告警的由来。
Props 表展示的分组顺序与字段来源在 extract-arg-types.ts 中定义:
const SECTION_ORDER = [
'properties',
'inputs',
'outputs',
'methods',
'view child',
'view children',
'content child',
'content children',
];
这与原文档“支持 inputs、outputs、properties、methods、view/content child/children 作为一等属性类型”的表述一一对应。该模块还提供 checkValidCompodocJson、findComponentByName、extractArgTypes、extractComponentDescription 等工具函数(通过 browser.ts 适配层 暴露),对应测试见 browser.test.ts 与 extract-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 既可作为 Meta 的 decorators(对整个文档页生效),也可直接写在单个 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.md;同目录还有 COMMON.md、REACT.md 等其他框架的 Docs 指南可对照阅读;
- 其他框架的 Compodoc 特性:从源码结构看,
@storybook/angular-vite框架包通过framework.options.compodoc选项控制是否执行 Compodoc(见 code/frameworks/angular-vite/src/builders/start-storybook/index.ts 的构建器实现),即 Vite 版 Angular 渲染器沿用了同一套@storybook/angular-compodoc元数据管线; - 官方文档站中的 Angular 相关配置片段可参考 angular-add-compodoc.md、angular-project-compodoc-config.md、angular-framework-options.md 等;
- Compodoc 元数据解析的单元测试:extract-arg-types.test.ts、angular-vite 的 compodoc.test.ts,其中演示了
vi.stubGlobal('__STORYBOOK_COMPODOC_JSON__', compodocJson)这一注入方式,印证了 preview 侧通过全局变量传递 JSON 的实现。
小结
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 共享包,配置层的心智模型都保持不变。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00