ARTICLE DETAIL

资讯详情

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

Appsmith 自定义 Widget 开发指南:掌握 Widget Development API,构建可注册、可复用的 UI 组件

Appsmith 自定义 Widget 开发指南:掌握 Widget Development API,构建可注册、可复用的 UI 组件 Appsmith 自定义 Widget 开发指南掌握 Widget Development API构建可注册、可复用的 UI 组件【免费下载链接】appsmithPlatform to build admin panels, internal tools, and dashboards. Integrates with 25 databases and any API.项目地址: https://gitcode.com/GitHub_Trending/ap/appsmith导读Widget组件是 Appsmith 应用中最基础的 UI 构成单元。这篇技术指南以仓库文档 AppsmithWidgetDevelopmentGuide.md 为核心骨架系统讲解 Appsmith 的Widget Development API从Appsmith 开发者如何配置一个组件的用户视角深入到Widget 开发者如何编写并注册一个组件的实现视角覆盖文件夹结构、组件注册、CONFIG配置项、属性面板配置、派生/默认/Meta 属性、Blueprint 与 Enhancements 等全部知识点。读完本文你将掌握把任意 React 组件接入 Appsmith 平台app/client/src/widgets的完整方法论并能基于 widgets/registry.ts 的注册机制与 BaseWidget 的继承体系开发出自己的平台级组件。一、名词约定术语含义Appsmith 开发者Appsmith developers使用 Appsmith 平台为最终用户构建应用的普通使用者Widget 开发者Widget developers开发组件、把组件提供给 Appsmith 开发者使用的开发者Entities实体Appsmith 应用的积木块包括 Widgets、Queries、APIs、appsmith.store与 JS ObjectsWidgets组件Appsmith 生态中的 UI 积木块类似任何设计系统里的组件用于在 Appsmith 中搭建界面理解这两类角色非常重要Appsmith 开发者决定“这个组件在应用里如何被配置”而 Widget 开发者决定“哪些属性、哪些事件能被暴露给 Appsmith 开发者”。Widget Development API 就是后者用来定义前者的边界与能力的编程接口。二、Widget 的两类对外交互属性绑定与动作触发一个组件一旦被放到画布上Appsmith 开发者主要与它产生两类交互读取/设置属性properties以及配置交互触发action triggers。2.1 Widget 属性与数据绑定属性是定义 Widget 状态的值。当 Appsmith 开发者希望用其它 Entity 的数据更新某个 Widget 的状态时就需要将其他 Entity绑定bind为该 Widget 的某个属性。以 Text Widget 为例它有一个text属性用于定义显示文本。若希望把某个 Input Widget 的值显示到 Text Widget 中就把 Input Widget 的text属性绑定给 Text Widget 的text属性{{ Input1.text }}其中Input1是输入框组件的名称text是它的文本属性{{ }}让平台求值括号内的内容——这意味着可以在{{ }}内写任意 JavaScript 来加工 Entity 的属性值例如{{ Input1.text.toLowerCase() }}绑定求值的背后是平台的一套求值管线。从当前仓库源码看绑定内容最终由求值模块解析并注入到渲染树中相关测试覆盖在 Evaluation 系列文件中这保证了{{ }}语法在 worker 线程中安全、增量地执行。2.2 Widget 动作触发Action Triggers许多组件是可交互的交互可以触发 Web 应用中的工作流——这正是静态网页与 Web 应用的分野。例如 Button Widget 具备“点击”这一交互因此平台暴露了onClick动作触发点其处理器handler可由 Appsmith 开发者配置。例如希望在点击按钮时弹出提示可以这样配置{{ showAlert(My message, info) }}showAlert是 Appsmith 平台提供的动作My message是显示在提示框里的字符串info是showAlert特有的参数描述消息类型。动作触发同样支持绑定 Entity 属性甚至结合 JS{{ showAlert(Text1.text, info) }} {{ showAlert(Text1.text.toLowerCase(), info) }}三、Widget Development API总体设计Appsmith 通过一套Widget Development API将 React 组件接入平台让组件能够被注册并在组件选择器中列出供 Appsmith 开发者使用。3.1 目录结构组件代码全部位于仓库的app/client/src/widgets目录每个组件拥有自己独立子目录。以 FormWidget 为例典型目录结构如下文件/目录职责index.ts存放组件的配置CONFIG默认导出组件类本身constants.tsx存放组件与其 component 使用的常量icon.svg代表该组件的图标 SVG 文件widget/index.tsx组件代码主体利用 Widget Development API 告诉平台“如何渲染这个组件”component/index.tsx真正渲染在主画布上的核心 React 组件这里涉及一个平台概念CanvasCanvas 是 Appsmith 平台中的一种特殊 WidgetAppsmith 开发者可以在其内部放置其它 Widget。例如 Container Widget 内部就含有一个 Canvas Widget从而允许在容器内继续放置组件。可以使用脚手架命令在 CLI 中一键生成上述目录结构cd app/client yarn generate:widget该命令由仓库中的 plop 生成器驱动定义在 generators/index.js组件模板位于 generators/widget以.hbs模板形式提供其中包含directory、suffixed将名称后缀为XXXWidget与widgetTypeFormat格式化为XXX_WIDGET等辅助函数。3.2 Widget 注册Registration组件要被 Appsmith 应用列出必须完成注册。以当前仓库实现为准注册通过“loader 注册表 动态导入”完成widgets/index.ts 汇总所有 Widget 的动态 import loader构成Mapstring, () Promisetypeof BaseWidget同时并入 EE 版 Widget 与 WDS Widget并在模块被引导加载时以副作用方式调用registerWidgetLoaderswidgets/registry.ts 维护WidgetLoaders与loadedWidgets两个 Map对外暴露registerWidgetLoaders、loadWidget(type)、loadAllWidgets()。其中loadWidget会缓存已加载组件并用retryPromise重试加载找不到类型时抛出Widget type ${type} not found。之所以采用“registry 间接层”而不是静态引用仓库源码注释给出了原因求值/编辑器依赖图中的消费者如EvaluationsSaga、EditorUtils需要loadWidget/loadAllWidgets而 Widget 配置模块又会反向依赖该依赖图直接引用 loader 会引入大型循环依赖。补充说明仓库早期版本及本文配套原始文档描述的是registerWidget(config)CONFIG的注册 API当前代码库已演进为上述 loader 注册与static getConfig()模式下文介绍的CONFIG概念与配置字段仍在现行组件类中延续。3.3 组件类与CONFIGWidget 的配置通常命名为CONFIG对象形式导出在index.ts文件默认导出必须是组件类本身。以 FormWidget/widget/index.tsx 为例当前代码通过静态方法返回配置class FormWidget extends ContainerWidget { static type FORM_WIDGET; static getConfig() { return { name: Form, iconSVG: IconSVG, thumbnailSVG: ThumbnailSVG, tags: [WIDGET_TAGS.LAYOUT], needsMeta: true, isCanvas: true, searchTags: [group], }; } static getDefaults() { return { rows: 40, columns: 24, borderColor: Colors.GREY_5, borderWidth: 1, widgetName: Form, children: [], blueprint: { /* ... */ }, }; } }可以看到 Form Widget 通过blueprint预置了画布内的默认子组件结构文本 提交按钮这与下文的Widget Blueprint机制一一对应。3.4 配置选项Configuration Options一份组件配置通常包含以下选项字段含义以原始文档为准配合现有源码佐证type必填组件的唯一类型标识旧 API 中为Widget.getWidgetType()。name必填组件在 UI 上的显示名可以包含空格。iconSVG必填从./icon.svg导入的图标。needsMeta可选若该组件需要存储临时值如用户输入或状态设为true。isDeprecated可选标记组件已废弃。为true表示不再推荐使用应引导开发者改用替代组件。isCanvas可选若该组件内部含有 Canvas、允许在其内继续放置 Widget设为true。properties必填通常聚合四类属性映射derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), meta: Widget.getMetaPropertiesMap(), config: Widget.getPropertyPaneConfig(),defaults必填组件的默认属性。平台已提供通用默认配置任何未在此定义的属性默认值为undefinedWidget 开发者必须对这类属性做防御处理。其中关键字段包括rows必填组件放到画布上时默认占用的行数columns必填组件放到画布上时默认占用的列数widgetName必填该类型组件自动生成名称的前缀不能含空格或特殊字符version必填该组件类型的版本号blueprint可选组件蓝图见下文enhancements可选叠加在组件上的增强能力。3.5 注意事项所有配置都会影响组件行为配置错误可能引发异常需谨慎。部分常量与类型由平台提供可从src/WidgetProvider/constants导入当前仓库对应 WidgetProvider/constants.ts内含网格密度迁移常量、日期格式选项、JSON Form 子组件样式表等。blueprint与enhancements是构造复杂组件的强大特性。四、Widget 类的静态方法与继承方法Widget 代码须全部位于widget目录widget/index.tsx导出继承BaseWidget定义于 widgets/BaseWidget.tsx的类。4.1 静态方法getPropertyPaneConfig必填返回属性面板整体配置。getPropertyPaneContentConfig必填返回属性面板“内容”分区配置。getPropertyPaneStyleConfig必填返回属性面板“样式”分区配置。getDerivedPropertiesMap可选返回可从其他属性推导出的属性映射见 5.1。getDefaultPropertiesMap可选返回默认取值来源于某默认属性的映射见 5.2。getMetaPropertiesMap可选返回将被视为 Meta 属性并存储的属性见 5.3。getWidgetType必填返回组件唯一的类型字符串。4.2 继承方法来自 BaseWidget 的公共 APIexecuteActionvoid执行某个动作通常用于调用已配置的 action trigger。参数triggerPayload若传入undefined/null会抛出错误。disableDragvoid禁止组件在画布中被拖拽。例如 Table Widget 在通过表头拖拽调整列顺序时会禁用自己的整体拖拽避免与平台特性冲突。参数disabletrue禁用拖拽false恢复。updateWidgetPropertyvoid更新单个组件属性。propertyPath待更新属性路径propertyValue目标值。deleteWidgetPropertyvoid删除某个组件属性。batchUpdatePropertyvoid批量更新多个属性。updatesBatchUpdatePropertyPayload数组shouldReplay为false时该更新不会进入撤销记录cmdz/ctrlz默认true。resetChildMetaPropertyvoid重置该组件所有子组件的 Meta 属性。widgetId当前组件 id。updateWidgetMetaPropertyvoid与updateWidgetProperty不同这类更新不会在刷新后持久化Meta 属性是典型的瞬态属性如用户输入。propertyPath必填属性路径propertyValue必填属性值actionExecution可选随属性更新一并执行的动作载荷。getPageViewReactNode必填React.render 的增强版本返回应在画布上渲染的 React 组件。五、三类属性映射机制5.1 派生属性Derived Properties派生属性由组件的其它属性计算得出。例如 Rich Text Editor 的isValid可由isRequired与text推导当组件被配置为必填而文本为空时应判为非法。一个 JS 条件表达式即可完成赋值{{ this.isRequired ? this.text this.text.length : true }}注意this代表组件自身的上下文这里是 Rich Text Editor。因此getDerivedPropertiesMap返回一个对象键为派生属性名值为一段计算派生属性值的 JS 绑定字符串。5.2 默认属性Default Properties默认属性映射定义了“其他属性从属性面板中的哪个默认配置取值”。仍以富文本编辑器为例text保存用户输入内容但也可以在属性面板配置一个起始默认值该配置项名为defaultText。通过getDefaultPropertiesMap声明text如何取得默认值static getDefaultPropertiesMap(): Recordstring, string { return { text: defaultText, }; }注意当defaultText出现新值时它会覆盖text的当前值。5.3 Meta 属性Meta PropertiesMeta 属性的值是瞬态的不会持久化到应用本身。例如富文本编辑器中用户输入的内容text不会被持久化但会驻留在内存中可被绑定表达式使用。通过getMetaPropertiesMap配置static getMetaPropertiesMap(): Recordstring, any { return { text: undefined, }; }注意若该属性同时还被getDerivedPropertiesMap等 API 使用其 Meta 值必须为undefined。六、属性面板配置Property Pane Configuration属性面板配置决定属性控件的顺序、校验规则、分组、控件类型等。类型为ArrayPropertyPaneConfig参考 constants/PropertyControlConstants.tsx。按面板分区Appsmith 又细分为**内容配置content与样式配置style**两类。6.1 PropertyPaneSectionConfig面板分区用于定义属性面板中的各个 SectionsectionName必填string分区显示名children必填PropertyPaneConfig[]通常是本分区要展示的一组属性控件见 PropertyPaneControlConfighidden可选boolean判定该分区是否隐藏的函数参数为props当前组件属性与propertyPath该分区相对组件的路径不在 panel 中时通常是组件自身。6.2 PropertyPaneControlConfig属性控件定义单个属性控件的配置label必填向 Appsmith 开发者展示的属性名propertyName必填与值关联的属性键helpText可选帮助文案悬停 label 时以 tooltip 呈现isJSconvertible可选是否允许 Appsmith 开发者使用 JS 按钮对本属性做绑定controlType必填控件类型如INPUT、SELECT等panelConfig可选若该属性会打开一个 Panel则在此定义 Panel 配置isBindProperty必填该属性值是否允许用绑定表达式定义isTriggerProperty必填为true表示这是可触发动作的事件处理器updateHook可选Array{propertyPath, propertyValue} | undefined当该属性被更新时用于同步更新其他属性的钩子。它在“新属性值被存储与求值之前”执行返回的所有属性更新将与本次更新同时生效。参数props组件属性、propertyName组件属性路径、propertyValue即将写入的新值返回值propertyPath/propertyValue对象数组或undefined。hidden可选返回true时隐藏该属性参数为props与propertyPathadditionalAutoComplete可选返回该属性额外的自动补全条目。返回类型Recordstring, Recordstring, unknown外层键为关键词内层对象键作为补全候选dependencies为updateHook/hidden必需string[]列出这两个函数计算所需的属性路径是一种性能优化——让平台只订阅这一小撮组件属性用于计算validation必填属性校验配置见下文customJSControl可选当需要以自定义控件替代标准INPUT控件时指定。6.3 PanelConfig面板配置用于描述 Panel 内展示的属性细节editableTitle必填Panel 标题是否可编辑titlePropertyName必填Panel 内属性的根路径children必填Panel 内的分区与控件配置也可包含updateHook逻辑Table Widget 的 propertyConfig 是典型实例。6.4 属性校验配置Property Validation Configuration当允许 Appsmith 开发者使用绑定表达式时平台会对属性做校验以维护组件完整性提供了校验的组件可以期望拿到“已校验”的属性值。type必填ValidationTypes执行的校验类型枚举定义于 constants/WidgetValidation.ts。params部分类型必填ValidationConfigParams辅助校验的参数常见包括min/max可选number用于ValidationTypes.NUMBER的最小/最大值natural可选配合NUMBER校验自然数default可选非法或undefined时的兜底默认值unique可选boolean | string[]指定需要唯一性的属性路径required可选boolean该属性是否为组件正常工作的必填项regex可选RegExpTEXT类型要匹配的正则allowedKeys可选数组OBJECT类型允许的键配置每项含name、type、paramsallowedValues可选unknown[]ARRAY类型允许的值集合children可选ValidationConfigOBJECT_ARRAY数组元素的校验配置fn可选FUNCTION类型使用的校验函数签名(value, props, _, moment) ValidationResponse其中_为 lodash 工具、moment为 momentjs 工具expectedFUNCTION类型必填描述期望类型、示例与自动补全数据类型含type、example、autocompleteDataTypestrict可选为true时TEXT值在校验前不会被强制转成字符串ignoreCase可选为true时OBJECT的allowedKeys匹配忽略大小写。所有校验最终都返回ValidationResponseisValid是否合法、parsed校验后的值可能是默认值、原始值或格式化结果、messages可选描述校验失败原因帮助 Appsmith 开发者定位问题。需要强调ValidationTypes.FUNCTION应谨慎使用仅在其他校验类型不满足需求时作为逃生通道escape hatch。七、Widget Blueprint组件蓝图与 Enhancements增强7.1 Blueprint 的定位Blueprint 是一种描述“组件子结构及其属性修改”的配置在 Appsmith 开发者把组件拖到画布时自动套用。例如 Form Widget 默认含一个 Canvas其内部预置了文本组件与按钮组件——这正是 FormWidget/widget/index.tsx 中getDefaults()返回的blueprint所配置的结构CANVAS_WIDGET下嵌套TEXT_WIDGET、BUTTON_WIDGET。Blueprint 由两部分构成view与operations。7.2 Blueprint View子结构描述view可选Array{ type, size, position, props }描述子组件及其排布type必填WidgetType子组件的类型size必填{ rows, cols }子组件占用的行、列数position必填{ top, left }子组件相对父组件的偏移props可选写入子组件的默认属性。需要注意若某个子组件需要同时拥有自己的子结构须在其props内再提供blueprint由于只有CANVAS_WIDGET能容纳子组件绝大多数 Blueprint 都以CANVAS_WIDGET起步其余嵌套子结构配置在它的blueprintprop 中CANVAS_WIDGET的size无需定义——它独立工作并占满父容器因此position恒为{ top: 0, left: 0 }。7.3 Blueprint Operations结构运算operations可选BlueprintOperation[]是在子结构上屏前执行的一批“属性修改”运算类型为{ type: BlueprintOperationType, fn: BlueprintOperationFunction }BlueprintOperationType共有三种MODIFY_PROPS修改属性ADD_ACTION添加 action trigger 处理器CHILD_OPERATIONS当有子组件被加入该组件时执行。7.4 Widget Enhancementsenhancements是对组件施加的“增强能力”。从仓库代码看典型例子是dynamicHeight动态高度在 FormWidget/widget/index.tsx 中static getFeatures()返回了dynamicHeight含sectionIndex与active平台据此在不改组件核心代码的情况下叠加交互与 UI 能力。这类特性被集中注册在 WidgetProvider/factory 与 widgets 各目录中并有专项单测如featurePropertyPaneEnhancement.test.ts覆盖。八、附加能力与最佳实践8.1 derived.js 与 parseDerivedProperties.ts部分组件如 Table/List的派生属性逻辑较复杂会单独抽到derived.js文件而parseDerivedProperties.ts负责把其中的函数以字符串形式加载供派生属性绑定使用。仓库实例包括ListWidget/widget/derived.js parseDerivedProperties.tsTableWidget/widget/derived.js parseDerivedProperties.ts类似的derived.js还广泛存在于 InputWidgetV2、SelectWidget、TabsWidget、DatePickerWidget2、CurrencyInputWidget 等组件中属于复杂派生逻辑的标准组织方式。8.2 Component Constants 与 Widget Constants平台为组件开发者在构建标准组件时提供了一批组件级常量见 components/constants.ts含按钮变体ButtonVariantTypes、验证码类型等组件级另有Widget 常量如网格默认值、Widget 标签等定义在 constants/WidgetConstants.tsx以及本文前述的 WidgetProvider/constants.ts。8.3 Widget 工具函数平台还提供工具函数帮助开发组件如自动补全定义基类DefaultAutocompleteDefinitions等见 widgets/WidgetUtils.tsForm Widget 源码中已实际引用。8.4 绑定如何被求值Appsmith 的绑定求值在浏览器端完成属于低代码产品的核心难点平台需要在编辑时、应用运行时都能安全地把{{ }}内的代码解析为可执行表达式并注入依赖。仓库中求值相关逻辑集中在app/client/src/workers/Evaluation与ce/workers/Evaluation等目录含大量单元测试是理解“绑定如何生效”的最佳源码入口。8.5 性能注意事项开发组件时应避免使用componentDidMount/componentDidUpdate这类生命周期更多依赖getDerivedPropertiesMap等机制在渲染前推导相关值。懒加载组件是很好的实践尤其是当该组件的实现引入了新库时。这一点在当前仓库中已成为标准做法——每个组件在 widgets/index.ts 中都通过动态import()注册 loader从而只在需要时才加载对应组件代码。结语从“绑定{{ Input1.text }}”这样的一行表达式到 Blueprint 自动搭建 Form 的子结构Appsmith 的 Widget 体系把 React 组件开放成了一等公民。若要落地开发自己的组件请以 widgets/FormWidget/widget/index.tsx 为活体范例、以 widgets/registry.ts 为注册入口遵循“widget逻辑component渲染CONFIG配置icon.svg图标”的结构进行扩展而属性面板的呈现与校验规则则由 constants/PropertyControlConstants.tsx 与 constants/WidgetValidation.ts 统一支撑。深入这套 API你就能把任意 React 组件平滑地转化为所有 Appsmith 开发者都可用的平台级组件。【免费下载链接】appsmithPlatform to build admin panels, internal tools, and dashboards. Integrates with 25 databases and any API.项目地址: https://gitcode.com/GitHub_Trending/ap/appsmith创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表