跨框架 Props 声明完全指南:让 Storybook 免费生成 argTypes、Controls 与 Docs 面板
跨框架 Props 声明完全指南让 Storybook 免费生成 argTypes、Controls 与 Docs 面板【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook在 Storybook 仓库的 docs/_snippets/ 目录里有一个被反复引用的Button示例一个布尔开关加一段文案却在 React、Angular、Vue、Svelte、Web Components 五种技术栈里各有一套完整实现。这篇指南不打算逐框架念稿而是沿着一条主线拆解——你在组件里写的类型与注释正是 Storybook 自动生成 argTypes、Controls 与 Docs 面板的数据源头。读完后你手里会有一套声明维度心智模型类型、默认值、必填、注释拿到任何框架的组件都能一眼看出它的文档面板长什么样。先看清下游docgen 如何把组件源码变成 argTypes为什么写组件时要纠结 Props 声明的写法因为你在 meta 里声明component: Button的那一刻Storybook 就会启动各框架对应的 docgen 链路去读你的组件源码把结果编译成一份argTypes结构——Controls 面板和 Docs 的 ArgsTable 全靠它渲染。这份推导结果长什么样可以直接看仓库里的示例 storybook-generated-argtypes.mdconst argTypes { label: { name: label, type: { name: string, required: false }, defaultValue: Hello, description: demo description, table: { type: { summary: string }, defaultValue: { summary: Hello }, }, control: { type: text, }, }, };对照这张结构你会发现type、defaultValue、description这三个字段没有一个是 Storybook 凭空猜的——它们分别来自你写的类型声明、默认值、JSDoc 注释。control.type: text则是由string类型推导出来的控件形态类型决定控件形态注释成为文档这是贯穿全文的两句话。各框架的 docgen 入口在仓库里都有据可查ReactVite 路线在 code/frameworks/react-vite/package.json 中依赖react-docgen与joshwooding/vite-plugin-react-docgen-typescriptdocgen 的 handler 与 resolver 定制在 code/frameworks/react-vite/src/plugins/ 下Webpack 路线则由 code/presets/react-webpack/package.json 引入storybook/react-docgen-typescript-plugin。Angularcode/frameworks/angular/package.json 依赖storybook/angular-compodoc通过 Compodoc 提取组件元信息其 builder 的compodoc/compodocArgs配置项见 code/frameworks/angular/build-schema.jsonAngular-Vite 另有一条内建 docgen worker 链路见 code/frameworks/angular-vite/package.json 的./internal/docgen-worker导出。Vue 3code/renderers/vue3/package.json 依赖vue-docgen-api由 code/renderers/vue3/src/docgen/build-docgen.ts 把组件元信息经extractArgTypes转成 argTypes源码里能看到argTypes: extractArgTypes({ __docgenInfo: componentMeta })这一行。Svelte走storybook/addon-svelte-csf的defineMeta读取组件注释。Web Components / Lit直接解析类上方 JSDoc 的prop标签与property()装饰器。下图是这套机制的最终产物——同一份组件 Props 声明在 VuePROPS、AngularINPUTS/OUTPUTS、Web ComponentsPROPS/EVENTS/CSS 自定义属性三种技术栈下的 ArgsTable 自动渲染效果列头统一是 Name / Description / Default所以结论很直白把组件声明写好 ≈ 免费获得文档页 调试面板。下面的章节就按四个声明维度来看同一份代码在各框架里分别怎么写。本文所有代码示意均取自 button-component-with-proptypes.md只保留与声明相关的核心行。跨框架类型声明的六种写法从 PropTypes 到装饰器先放一张紧凑对照再逐条点评框架类型写在哪里示意React (JS)组件外propTypes对象isDisabled: PropTypes.bool.isRequiredReact (TS)独立 interface React.FC泛型isDisabled: booleanAngular类字段上的Input()装饰器Input() isDisabled: booleanVue 3props选项的运行时对象isDisabled: { type: Boolean, ... }Sveltescript内export let变量export let disabled falseWeb ComponentsLitstatic get properties()或property()装饰器content: { type: String }/property() content?: string几个值得注意的差异化细节React 是唯一把类型声明放在组件外部的框架JS 版挂在Button.propTypes上运行期校验TS 版挂在一个独立的ButtonPropsinterface 上编译期校验。interface 字段未加?时两个属性都是必填而React.FCButtonProps的泛型标注正是 docgen 把 interface 和组件关联起来的钩子。Vue 的props用的是运行时构造器Boolean/String不是 TS 类型。这意味着即使script langts里用defineComponent包了一层类型推导也只能覆盖到一半——type: String对 docgen 来说就是string而 TS 侧的联合类型收窄比如small | medium | large是表达不出来的得靠validator函数补位。Svelte 没有类型声明这一层export let本身既是声明也是导出类型完全靠 Svelte 编译器推断写注释的人得自己保证描述准确。Web Components 是六种里最啰嗦的JS 版要手写static get properties()并在构造函数里赋值TS 版换成customElementproperty()装饰器后content?: string One一行就把声明、可选性、默认值全包了。跨框架默认值的三种写法解构参数、字段初值、default 字段同样是按钮默认可点击、文案默认 One六个实现里藏着三种流派流派框架代码形态解构参数默认值React (TS)({ isDisabled false, content })字段初值Angular / Svelte / Web ComponentsInput() isDisabled: boolean字段声明无初值export let disabled falsethis.isDisabled false构造函数或isDisabled?: boolean false字段装饰器default字段Vue 3isDisabled: { type: Boolean, default: false }点评三处不一致React 的 JS 与 TS 版本策略相反PropTypes 版把两个属性都声明为isRequired、不给默认值强制调用方负责TS 版却在解构里给了 false/ 组件自带兜底。同一仓库、同一个按钮两种契约并存——你在 Storybook 里看到的table.defaultValue是否出现取决于你抄的是哪个版本。Vue 用default字段最显式但也埋了个雷JS 版里isDisabled同时写了default: false和required: true。Vue 的语义是required 优先于 default两者并存属于互相矛盾实际项目里二选一即可增强版示例 button-implementation.md 就只保留了default。Angular 与 Svelte 用有无初值暗示可省略Svelte 的export let disabled false天然表达可选 有默认Angular 示例里字段没有初值若组件要能独立渲染建议补 false。必填语义的四种表达isRequired、required、required 与默认值约定必填是四个维度里最不成文法的一个各框架各说各话表达方式框架备注isRequired链式标记React (JS)PropTypes.bool.isRequired运行期缺失时控制台告警required: true字段Vue 3与default语义互斥见上文挑刺JSDocrequired注释Angular / Svelte纯注释约定靠 docgen 工具解释button-implementation.md 的 Angular 版给label打了required默认值约定Web Components / 各框架 TS 版不显式声明有默认值 可省略最容易被忽略的一点React 的 TS 版本把必填信息从isRequired换成了 interface 里字段不带?这一静态手段isRequired在 TS 版里直接消失。也就是说同一个组件从 JS 迁到 TS 后必填语义的载体从运行期标记变成了类型系统docgen 的产出不会变但保证它的机制变了。跨框架阅读组件时看到?、isRequired、required: true、required任何一种都应该翻译成同一句话这个参数不给会怎样。JSDoc 注释与 description 的对应关系注释写在哪里文档就在哪里最后一个维度决定 Docs 面板里Description那一列有没有内容。对应关系其实很机械——注释紧邻哪个声明哪个 argType 就拿到 descriptionscript /** * Disable the button * required */ export let disabled false; /** * Button content * required */ export let content ; /script button typebutton {disabled}{content}/buttonReact注释紧贴propTypes的 keyJS 版或 interface 字段TS 版。注意 button-component-with-proptypes.md 里 React/Angular/Vue 的属性名是contentSvelte 却叫disabled/content、Vue 模板里渲染的是label——同一语义在不同框架示例里命名并不统一跨框架对照阅读时先对一遍名字。Angular / Vue注释写在Input()字段或props项上方Compodoc 与vue-docgen-api都按属性上方第一个 JSDoc 块的规则提取。Web Components 是唯一把注释上移到类头部的框架/** * prop {string} content - The display label of the button * prop {boolean} isDisabled - Checks if the button should be disabled * summary This is a custom button element * tag custom-button */ export class CustomButton extends LitElement { static get properties() { return { content: { type: String }, isDisabled: { type: Boolean } }; } // ... }这里prop把类型 描述合写在一个标签里tag声明自定义元素名与customElements.define(custom-button, ...)一致summary成为组件级摘要——TS 装饰器版则保留同一块类头注释让 docgen 从property()解析出相同信息。还有一个藏在 Svelte 示例里的小坑值得专门提一句原始片段的脚本块写的是script/这种自闭合形态Svelte 编译器并不接受它实际项目里必须闭合为/script否则整个组件构建失败。照抄教学片段前这类差一个斜杠的问题最好先过一遍编译。六框架 Props 声明对照总表与共性规律把四个维度压回一张表代码全文见 button-component-with-proptypes.md框架声明位置类型来源默认值写法必填表达说明来源组件名React (JS)Button.propTypesPropTypes.*无全 isRequiredisRequiredkey 上方 JSDocButtonReact (TS)ButtonPropsinterfaceTS React.FC泛型解构默认值字段不带?字段上方 JSDocButtonAngularInput()类字段字段 TS 类型字段初值示例未给JSDocrequired字段上方 JSDocmy-buttonVue 3 (JS/TS)props选项运行时type构造器default字段required: true项上方 JSDocbuttonSvelteexport let编译器推断变量初值required注释变量上方 JSDocButtonWeb Components (JS)static get properties()type: String/Boolean构造函数赋值默认值约定类头propcustom-buttonWeb Components (TS)property()字段Lit 装饰器 TS字段初值默认值约定类头propcustom-button共性规律三条注释即文档是全框架公约数无论挂在 propTypes、interface、Input、export let还是类头prop上这段文字最终都会落进 argTypes 的description成为 ArgsTable 的 Description 列与 Controls 面板的提示文字。类型驱动控件、默认值进表格boolean自动渲染为开关、string渲染为文本框默认值则出现在table.defaultValue汇总里——这两项缺失Docs 页的 Props 表立刻残废。必填语义各说各话isRequired、required: true、required、有默认值即可省四种表达共存且必填 默认值这种组合在 Vue 示例里就出现过并存矛盾。写组件前先在团队里约定一种表达比事后统一成本低得多。声明完成之后一个最小 meta 与跨框架工程习惯组件声明就位后剩下最后一步在.stories文件里用 CSF 引入组件并声明component把元数据与 Story 关联起来完整跨框架版本见 button-story-matching-argtypes.md// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. import type { Meta } from storybook/your-framework; import { Button } from ./Button; const meta { component: Button, parameters: { actions: { argTypesRegex: ^on.* } }, } satisfies Metatypeof Button; export default meta;这里parameters: { actions: { argTypesRegex: ^on.* } }会把所有on前缀的属性如onClick自动挂到 Actions 面板做调用记录详见 docs/essentials/actions.mdx。而 Web Components 由于组件是以字符串标签名注册的meta 里写的是component: demo-button这样的字符串而非类引用——这也是唯一一个组件引用方式与其他框架不同的地方。总结一下跨框架统一的工程习惯类型写准Controls 才有正确的控件默认值给全Docs 表格才完整必填语义选一种并全团队统一注释贴着声明写ArgsTable 才不空。这四项与框架无关是写组件这一步为写 Story铺路的完整交付清单——做到位Storybook 的构建、文档化与测试能力才能真正开箱即用下一步如何围绕这个组件写 Story可继续参考 docs/get-started/whats-a-story.mdx 与 docs/writing-stories/args.mdx。【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考