ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Storybook Button 组件 Props 声明指南:8 种框架写法自动生成 argTypes 与 Controls 面板

Storybook Button 组件 Props 声明指南:8 种框架写法自动生成 argTypes 与 Controls 面板 Storybook Button 组件 Props 声明指南8 种框架写法自动生成 argTypes 与 Controls 面板【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybookStorybook 的 Button 组件示例把 Props 声明当成喂给工具的数据来写React、Angular、Vue 3、Svelte、Lit 五套框架共 8 份实现分别由 react-docgen、Compodoc、vue-docgen-api 等 docgen 工具文档元信息生成器读取自动产出 argTypes——驱动 Controls 面板与 Docs 的元数据结构。本文按声明流派组织逐个拆解写法、解析原理与跨框架坑。声明即数据源component 一绑定Controls 为何自己长出来 在Button.stories.ts里写出component: Button的那一刻你什么都没干Controls 面板却已经长出来了isDisabled是一枚布尔开关content是一个文本框Docs 页的 ArgsTable 也自动排好了对应的行。这份凭空出现的面板背后是一份 argTypes。触发点是 meta 中的component字段。Storybook 拿到组件引用后按框架调用对应的 docgen 工具去读源码React 走react-docgenTypeScript 工程由react-docgen-typescript插件接管Vue 3 渲染器依赖vue-docgen-apiAngular 侧执行 CompodocSvelte 由storybook/addon-svelte-csf负责Lit 则直接解析 JSDoc 与装饰器。各工具读出的类型、默认值、必填信息最终翻译成统一结构const 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.name决定控件形态boolean渲染为开关string渲染为文本框control字段只是这次推导的落点description的原文来自属性上方或类上方的 JSDoc 注释一字不改地进入 Docs 面板defaultValue来自声明里的默认值解构初值、props 的default字段、构造函数或字段初始值位置不同读法一致table是type与defaultValue的摘要专供 Docs 的 ArgsTable 渲染这份结构可以直接在 docs/_snippets/ 的storybook-generated-argtypes.md片段中对照官方教程用它解释面板上的每个格子从哪来。写组件这一步因此是在给 Storybook 备料料备好了面板、文档、测试三件套才自动运转。三种 Props 声明流派八份实现的归类读法 按类型信息存在哪里、谁来校验划分8 份实现归为三派运行时校验派React PropTypes、Vue props 对象、编译期类型派React TS interface、Vue defineComponent、Lit 装饰器、注释驱动派Angular Input、Svelte export let、Lit 类级 prop。同一派内部对照差异只在容器形态跨派对照差异在校验时机与 docgen 的读取对象。运行时校验派React PropTypes 与 Vue props 对象这派把类型信息编码在运行时数据结构里docgen 读取的就是这份结构。React 侧propTypes挂在组件导出上每个字段由PropType 构造器 是否 isRequired 上方注释组成import React from react; import PropTypes from prop-types; export function Button({ isDisabled, content }) { return ( button typebutton disabled{isDisabled} {content} /button ); } Button.propTypes { /** Checks if the button should be disabled */ isDisabled: PropTypes.bool.isRequired, /** The display content of the button */ content: PropTypes.string.isRequired, };要点校验发生在开发环境的运行时调用方漏传或传错类型时PropTypes 往控制台写警告生产构建中该逻辑被摇掉isRequired标记让 docgen 在 argTypes 的type里生成required: truePropTypes.bool/PropTypes.string决定type.name也就决定了开关还是文本框注释块必须紧贴字段行书写react-docgen按紧邻声明规则提取位置偏移一个空行 description 就会丢这份实现没给默认值content必填且无defaultValueControls 文本框初始为空调用方必须自己传文案。Vue 侧的容器换成 SFC 的props选项三元组改由type/default/required三个字段承载template button typebutton :disabledisDisabled{{ label }}/button /template script import { reactive } from vue; export default { name: button, props: { /** * Checks if the button should be disabled */ isDisabled: { type: Boolean, default: false, required: true, }, /** * The display label of the button */ label: { type: String, default: One, required: true, }, }, setup(props) { props reactive(props); return { /** * What will be returned here will available to the component * Functions referenced here will act like methods */ }; // }, }; /script要点vue-docgen-api在构建期静态解析 SFC逐字段映射到 argTypestype进type.namedefault进defaultValuerequired进required注释进descriptiontype: Boolean/String同时承担运行时类型检查与文档来源双重角色setup(props)的返回对象是模板可用成员的暴露口示例里为空对象对 docgen 无影响——它只读props选项name: button是单字组件名会被vue/multi-word-component-names规则告警button-implementation.md 片段里的官方写法是在 name 上方加// eslint-disable-next-line vue/multi-word-component-names显式豁免本派最大的坑isDisabled同时写了default: false和required: true。Vue 运行时的判断逻辑里default的存在让required: true形同虚设docgen 生成 argTypes 时以default为准必填标记被默认值盖掉——面板上看到的是有默认值的可选属性。编译期类型派React TS interface、Vue defineComponent 与 Lit 装饰器这派把类型交给编译器docgen 做的是静态 AST 读取不执行任何代码。React 的 TypeScript 版用 interface 承担类型与文档双重职责export interface ButtonProps { /** * Checks if the button should be disabled */ isDisabled: boolean; /** The display content of the button */ content: string; } export const Button: React.FCButtonProps ({ isDisabled false, content }) { return ( button typebutton disabled{isDisabled} {content} /button ); };要点调用方漏传字段TS 编译器直接报错这是运行时校验派做不到的强度docgen 由react-docgen-typescript插件完成框架仓库的 React Vite 预设依赖该插件见 code/frameworks/react-vite/在构建期解析 interface字段类型进type无?标记进required: trueJSDoc 进description解构参数里的isDisabled false、content 不会执行但 docgen 会读到落成defaultValue与 ArgsTable 的默认值列坑在 interface 层与运行层各说各话两个字段没标?类型层必填解构却给了默认值运行时拿得到值。面板最终呈现必填 有默认值的矛盾状态。想消除把 interface 字段加?或去掉解构默认值二者取一同一份 interface加不加?就是 Controls 上必填星号的全部来源粒度由你控制。Vue 的 TypeScript 版用defineComponent包住选项对象换回选项级类型检查template button typebutton :disabledisDisabled{{ label }}/button /template script langts import { defineComponent } from vue; export default defineComponent({ name: button, props: { /** * Checks if the button should be disabled */ isDisabled: { type: Boolean, default: false, }, /** * The display label of the button */ label: { type: String, default: One, required: true, }, }, setup(props) { /** * What will be returned here will available to the component * Functions referenced here will act like methods */ }, }); /script要点setup(props)的props参数被完整推导漏写属性名会编译期报错isDisabled去掉了required: true只留default: false——相比 JS 版这是同一属性必填 默认值矛盾写法的修正结果label保持required: true与default: One并存矛盾仍在与 JS 版共享同一遗留问题docgen 输入与 JS 版相同仍是运行时type字段TS 只影响开发期体验不改变 argTypes 的生成路径单字name的告警处理与 JS 版一致。Lit 的装饰器版本把声明压缩到字段上import { LitElement, html } from lit; import { customElement, property } from lit/decorators.js; /** * 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 */ customElement(custom-button) export class CustomButton extends LitElement { property() content?: string One; property() isDisabled?: boolean false; render() { return html button typebutton ?disabled${this.isDisabled}${this.content}/button ; } }要点customElement(custom-button)一步完成类装饰与自定义元素注册替代手写的customElements.defineproperty()让字段成为可观察属性Lit 在赋值时触发更新docgen 从装饰器语法推断type从类上方 JSDoc 的prop提取descriptiontag提供元素名content?: string One的字段初始值就是默认值来源?让 TS 允许省略实际运行值仍取自初始值坑与 React TS 同源可选标记 字段初始值的写法让类型层与运行层信号不一致另外 TS 装饰器语法需要构建配置开启experimentalDecorators漏配时编译直接失败类上方 JSDoc 是这派里唯一被 docgen 完整消费的部分字段注释在 Lit 解析链路中权重较低别把描述写错位置。注释驱动派Angular Input、Svelte export let 与 Lit 类级 prop这派把 JSDoc 本身当元数据注释标记被解析成结构化信息类型信息要么取自字段声明要么干脆只存在于注释里。Angular 用Input()装饰器 字段上方 JSDocimport { Component, Input } from angular/core; Component({ selector: my-button, template: button typebutton [disabled]isDisabled {{ content }} /button, styleUrls: [./button.css], }) export class ButtonComponent { /** * Checks if the button should be disabled */ Input() isDisabled: boolean; /** The display content of the button */ Input() content: string; }要点Compodoc 解析器扫描带Input()的字段字段 TS 类型进typeJSDoc 正文进description注释里的required标记进必填位解析产物是__docgenInfo结构webpack 侧由 code/frameworks/angular/ 的storybook/angular-compodoc执行Angular Vite 框架内置了自己的 docgen workerselector: my-button受 Angular 双词规则约束连字符 至少两段避免与原生 HTML 标签撞名selector: button会被编译器拒绝属性上方加requiredJSDoc 标记是 Compodoc 约定的必填写法见 button-implementation.md 的 Angular 示例Angular 本身没有运行时必填校验本例两个字段都未赋初值[disabled]isDisabled在值为undefined时按假处理按钮可点——无初值在这派里不等于必须传。Svelte 的export let把声明、默认值、注释压缩到一行script /** * A Button Component * component */ /** * Disable the button * required */ export let disabled false; /** * Button content * required */ export let content ; script/ button typebutton {disabled}{content}/button要点svelte-docgen 读取export let的声明与紧邻 JSDoc变量类型进type初值进defaultValuerequired进必填位export let disabled false一行同时承担默认值与对外声明两个职责{disabled}是disabled{disabled}的语法糖required、component都是 docgen 约定的注释标签运行时不读删掉标签程序照常运行只是文档缺字段属性名是disabled而不是其他框架的isDisabled——同一语义跨框架换名后文专门处理片段中的script/是自封闭写法Svelte 编译器只认/script闭合标签照抄会直接编译失败docgen 读不到语法错误的源码复制到项目前先修这个标签。Lit 的 JS 版本把全部元信息压在类上方 JSDoc 与static get properties()import { LitElement, html } from lit; /** * 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 }, }; } constructor() { super(); this.content One; this.isDisabled false; } render() { return html button typebutton ?disabled${this.isDisabled}${this.content}/button ; } } customElements.define(custom-button, CustomButton);要点properties里的type: Boolean/String声明可观察属性及反射转换规则docgen 据此推导type.name构造函数里的this.content One、this.isDisabled false是默认值的唯一来源docgen 的读取对象是这段赋值语句prop {string} content - 描述的行内格式是这派的招牌类型写在花括号里描述用短横线分隔tag必须与customElements.define的注册名一致否则 meta 绑定与文档对不上注释驱动没有运行时校验层属性传错类型不会报任何警告这是三派中最软的一种?disabled是 Lit 模板的布尔属性绑定语法与声明层的type: Boolean各司其职。同名属性五个名字跨框架命名的对照成本默认值的落点已经分岔——React TS 在解构处、Vue 在default字段、Lit 在构造函数或字段初始值——属性名本身也在漂移。八份实现里禁用这一语义有两个名字React、Angular、Lit 用isDisabledSvelte 用disabled。展示文本同样分裂React 与 Angular 叫contentVue 叫labelSvelte 与 Lit 叫content。默认值策略则四分五裂React JS 版必填且无默认React TS 版可选且有默认Vue 两版必填且带默认矛盾写法Lit 两版可选且有默认Svelte 有默认但 JSDoc 标了required。对照成本落在三处团队跨框架读代码要维护一份映射表docgen 输出因策略不同而不同table.defaultValue列时有时空meta 里写 args 时名字跟哪份实现走没有统一答案。实现必填语义默认值写法docgen 工具备注React PropTypesJSisRequired后缀无react-docgen描述取自字段上方 JSDocReact TS interfaceinterface 无?解构初值react-docgen-typescript必填与默认值信号可能冲突Vue OptionsJSrequired: truedefault字段vue-docgen-api必填与默认并存属矛盾写法Vue defineComponentTSrequired: truelabel 保留default字段vue-docgen-apiTS 只改开发期体验不改生成路径Lit 装饰器TS无强制语义字段初始值Lit 装饰器解析?与初始值信号不一致Angular InputJSDocrequired无初值Compodocselector 受双词规则约束Svelte export letJSDocrequiredexport let初值svelte-docgen属性名disabled与他派不同Litstatic propertiesJS无强制语义构造函数赋值JSDocprop解析注释是元数据唯一来源收敛的办法只有一条团队约定一份属性命名与默认值策略写进组件头部注释各框架实现照抄。Controls 面板展示的就是这份声明——声明写岔面板先暴露。离第一个 Story 只差一个 meta组件声明收口之后CSF3 的 stories 文件只做一件事把组件和元数据绑进meta。// 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;要点component字段是 docgen 的触发点argTypes、Controls、Docs 的 ArgsTable 都从这一行开始生成satisfies Metatypeof Button保留meta的字面量类型字段写错在编译期暴露actions.argTypesRegex: ^on.*把以on开头的属性如onClick接入 Actions 面板记录调用本例 Button 没有事件属性该配置无副作用可留作后续扩展Web Components 的 meta 里component要写元素名字符串如custom-button与customElements.define的注册名一致换框架只改storybook/your-framework的包名与导入路径parameters部分跨框架通用详见 button-story-matching-argtypes.md。到此组件声明到面板生成的闭环成立写 Props 即备数据绑 meta 即投产Story 文件本身只剩展示差异。落地前自查清单JSDoc 注释紧贴声明行PropTypes 字段、interface 字段、Input()字段、export let变量、类上方prop任一位置偏移都会让description空掉。必填属性不带默认值isRequired与解构默认值、required: true与default并存时docgen 以默认值优先必填标记形同虚设。docgen 工具链已随框架预设启用React Vite 依赖 react-docgenVue3 依赖 vue-docgen-apiAngular 需 Compodoc缺失时 argTypes 为空Controls 降级成无类型输入框。元素名与组件名合规且与 meta 一致Angular selector 连字符双词、Vue 单字名有禁用注释、Littag与customElements.define同名。默认值写法跨框架统一同一语义属性在解构初值、default字段、构造函数赋值之间选定一种位置避免 ArgsTable 的默认值列各页不同。【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表