ARTICLE DETAIL

资讯详情

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

NocoBase 流程引擎 FlowModel 注册指南:从自定义模型到 registerModelLoaders 源码级解析

NocoBase 流程引擎 FlowModel 注册指南:从自定义模型到 registerModelLoaders 源码级解析 NocoBase 流程引擎 FlowModel 注册指南从自定义模型到 registerModelLoaders 源码级解析【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase导读本文聚焦 NocoBase 流程引擎Flow Engine中的FlowModel 注册机制如何自定义一个 FlowModel 子类、如何通过插件把模型注册到引擎、以及registerModelLoaders异步注册背后的完整工作原理。读完本文你将掌握在 NocoBase 客户端插件中注册自有模型区块、操作、数据表区块等的标准姿势并理解动态加载、继承声明、失败兜底等底层设计能够直接在自己的插件中落地一套可复用的自定义模型注册流程。FlowModel 是什么在 NocoBase 的流程引擎中FlowModel是所有页面元素模型的基类。无论是页面上的一个区块、一个操作按钮还是一个数据表展示最终都会被抽象成某个继承自FlowModel的模型类Model Class并通过引擎创建出对应的模型实例Model Instance参与渲染与交互。从源码看FlowModel定义于 packages/core/flow-engine/src/models/flowModel.tsx它是一个功能完备的基类支持define()静态元数据声明、子模型subModels创建、动作与事件注册通过类级ModelActionRegistry/ModelEventRegistry、国际化、菜单扩展、持久化等能力。引擎内部也会在构造时默认注册FlowModel本身// packages/core/flow-engine/src/flowEngine.ts constructor() { this.reactView new ReactView(this); this.flowSettings new FlowSettings(this); this.flowSettings.registerScopes({ t: this.translate.bind(this) }); this.registerModels({ FlowModel }); this.registerResources({ FlowResource, SQLResource, APIResource, SingleRecordResource, MultiRecordResource }); ... }这意味着所有模型类共享同一套基础设施自定义模型只需要继承它并声明自己的渲染逻辑即可。可用的 FlowModel 基类在动手之前先了解官方文档给出的三个可直接继承的基类基类名称说明BlockModel所有区块的基类CollectionBlockModel数据表区块继承自 BlockModelActionModel所有操作的基类选型原则很简单想要做一个区块页面中占据一块区域、负责展示内容的模型继承BlockModel想要做数据表区块绑定某个数据表/数据源展示表格数据的区块继承CollectionBlockModel想要做操作点击后触发动作的按钮类模型继承ActionModel。由于这些基类同样继承自FlowModel因此文档中从自定义 FlowModel 开始的写法对它们全部适用——你既可以继承最底层的FlowModel也可以直接继承上表中的语义化基类后者会自动获得区块/操作相关的公共能力。从自定义 FlowModel 开始第一步编写模型类最简单的自定义模型只需要继承FlowModel并实现render()方法返回一段 JSXclass HelloModel extends FlowModel { render() { return ( div h1Hello, NocoBase!/h1 pThis is a simple block rendered by HelloModel./p /div ); } }render()是模型类提供给渲染层的核心入口引擎的渲染器如FlowModelRenderer会调用它产出最终界面。自定义模型可以在这里组合任意 React 组件形成完全受控的自有区块。第二步通过插件注册到引擎模型的注册发生在客户端插件的load()生命周期中。以HelloModel为例一个完整的插件定义如下export class PluginHelloClient extends Plugin { async load() { this.engine.registerModelLoaders({ HelloModel: { // 动态导入首次真正用到这个 model 时才会加载对应模块 loader: () import(./HelloModel), }, }); } }这里的关键 API 是this.engine.registerModelLoaders(...)其中this.engine即流程引擎实例registerModelLoaders接收一个以模型类名为 key、以loader 配置为 value 的映射对象。仓库中一个可直接运行的最小示例见 docs/docs/cn/flow-engine/_demos/register-flow-model.tsx它把PluginHelloClient注入Application并导出根组件展示了插件 → 引擎 → 模型加载器的完整接线方式import { Application, Plugin } from nocobase/client-v2; export class PluginHelloClient extends Plugin { async load() { this.engine.registerModelLoaders({ HelloModel: { loader: () import(docs/cn/flow-engine/_demos/HelloModel), }, }); } } const app new Application({ plugins: [PluginHelloClient], }); export default app.getRootComponent();注册完成之后引擎便可以通过use: HelloModel之类的字符串引用在模型树model tree中创建该模型的实例页面和插件都能直接使用它。registerModelLoaders 的完整配置registerModelLoaders的类型定义位于 packages/core/flow-engine/src/types.ts它接受的输入为FlowModelLoaderInputMapexport interface FlowModelLoaderInput { loader: FlowModelLoader; extends?: string | ModelConstructor | (string | ModelConstructor)[]; }loader动态加载函数loader是返回 Promise 的模块加载函数类型为() PromiseFlowModelLoaderResult。FlowModelLoaderResult支持三种形态加载结果会被统一规范化export type FlowModelLoaderResult | ModelConstructor // 直接返回模型构造函数 | { default?: ModelConstructor; ... } // ES Module 的 default 导出 | Recordstring, unknown; // 包含具名导出的模块对象规范化逻辑见 packages/core/flow-engine/src/flowEngine.ts 中的normalizeModelLoaderResult先判断是否为函数若是对象则依次尝试default导出与以模型名命名的具名导出全部失败时打印警告并返回null。实际编码时最常见的写法是配合 Vite/webpack 的静态分析loader: () import(./HelloModel), // 模块内 default 导出模型类或者从模块对象中按具名导出取模型类这两种写法normalizeModelLoaderResult都能正确处理。extends声明继承关系可选extends字段用于声明该模型继承的父类可传字符串类名、构造函数或它们的数组注册时会被统一规范化为字符串数组见registerModelLoaders内部实现flowEngine.registerModelLoaders({ DemoModel: { extends: BaseModel, // 字符串形式 loader: () import(./models/DemoModel), }, });extends声明的核心价值在于异步子类发现引擎提供的getSubclassesOfAsync(baseClass)会合并两类结果——已经加载完成的类_modelClasses以及尚未加载但extends声明中包含该基类的 loader 条目见 packages/core/flow-engine/src/flowEngine.ts。也就是说即使某个子类模块从未被加载只要它声明了extends: BaseModel引擎就能在查询 BaseModel 子类时自动把它拉起来。加载完成后引擎还会用isInheritedFrom做一次真实的继承关系校验声明与实际继承不符的模型会被警告并跳过避免误导性注册。底层原理注册、加载与实例化的完整链路registerModelLoaders只是入口背后的异步加载链路才是这套机制的精髓。结合 packages/core/flow-engine/src/flowEngine.ts 源码整个链路如下1. 注册阶段registerModelLoaders(loaders)会把每个条目归一化后写入_modelLoadersMap并处理extends的规范化。同名字的 loader 重复注册会打印警告并覆盖旧条目同时它会将预加载标记_modelLoadersPreloaded重置为false确保新注册的 loader 能被后续的预加载流程拾取。2. 加载阶段ensureModel当引擎需要某个模型类时会走ensureModel(name)先从_modelClasses检查是否已注册命中直接返回检查是否有正在进行的加载 Promise_loadingModelPromises命中则复用同一个 Promise保证并发调用下 loader 只执行一次否则从_modelLoaders取条目并执行entry.loader()加载结果经normalizeModelLoaderResult规范化后通过_registerModel(name, modelClass)写入_modelClasses写入时还会用Object.defineProperty把类的name固定为注册名loader 抛错或结果不合法时打印警告并返回null不会中断整体流程。3. 预加载preloadModelLoaders对于不知道接下来会用到哪些模型的入口例如流程设置面板、模型发现类 UI引擎提供preloadModelLoaders()它一次性把_modelLoaders中所有尚未解析的条目全部加载。该方法具有幂等性——_modelLoadersPreloadPromise会缓存进行中的预加载 Promise加载完成后再次调用返回空结果loaded: []。4. 模型树解析resolveModelTree模型之间可以嵌套subModels因此在同步创建实例前需要先把模型树中所有以字符串形式引用的模型类加载完毕。resolveModelTree(data)会递归收集模型树中所有use字段为字符串的模型名含subModels层级逐批ensureModels加载加载成功的类的meta.createModelOptions里若还引用其他字符串模型会继续入队直到没有新的待解析项返回{ requested, loaded, failed }统计结果。这也解释了异步 API 的用途createModelAsync、getModelClassAsync、getModelClassesAsync、loadOrCreateModel都会先确保模型树中的字符串引用被解析再执行同步创建/查询。5. 实例化阶段createModelcreateModel(options)通过options.use字符串类名或构造函数找到模型类并new出实例如果模型类解析失败会降级创建ErrorFlowModel实例并通过setErrorMessage写入错误信息同时实例仍然正常加入_modelInstances注册表并触发model:created生命周期事件。也就是说一个模型加载失败不会拖垮整个页面渲染而是以一个可视化错误占位的形式呈现。同步注册与异步注册的选择除了registerModelLoaders异步、按需加载引擎还提供同步注册 APIregisterModelsflowEngine.registerModels({ UserModel, OrderModel });两种方式的取舍方式API加载时机适用场景同步注册registerModels(models)注册即生效模型代码量小、与主包一同打包、需要立即可用异步注册registerModelLoaders({ name: { loader } })首次使用时动态 import模型体积大、希望代码分割、减少首屏体积文档推荐并在示例中使用的是registerModelLoaders其核心收益正如代码注释所言首次真正用到这个 model 时才会加载对应模块配合动态import()可实现模型级代码分割。失败兜底与测试验证这套注册机制的可靠性在测试中得到了充分验证测试文件见 packages/core/flow-engine/src/tests/flowEngine.modelLoaders.test.ts覆盖了以下关键场景显式模型树与 meta 默认子模型的解析loadOrCreateModel能同时解析subModels中显式声明的子模型和父类meta.createModelOptions中声明的默认子模型且每个 loader 只执行一次仓库数据驱动的模型树解析从IFlowModelRepository.findOne读出的数据usesubModels树同样能在loadModel创建实例前完成解析loader 解析失败降级loader 返回不含合法模型的对象时createModelAsync结果降级为ErrorFlowModel父模型成功但子模型失败时父模型正常创建、子模型位置得到ErrorFlowModel整个创建流程不崩溃预加载幂等resolveModelTree之后调用两次preloadModelLoaders第二次loaded为空数组证明重复预加载不会重复执行 loader预加载后可追加注册预加载完成后再次registerModelLoaders注册新模型再次预加载能正确拾取新条目。完整落地示例插件中注册数据表区块把上面所有知识点串起来一个在插件中注册自定义数据表区块的完整流程如下// 1. 定义模型类继承 CollectionBlockModel数据表区块基类 import { CollectionBlockModel } from nocobase/client-v2; export class OrderBlockModel extends CollectionBlockModel { render() { return ( div h2订单区块/h2 {/* 这里可以组合任意 React 组件与数据源渲染逻辑 */} /div ); } } // 2. 插件中注册异步加载 继承声明 import { Plugin } from nocobase/client-v2; export class PluginOrderBlock extends Plugin { async load() { this.engine.registerModelLoaders({ OrderBlockModel: { extends: CollectionBlockModel, // 声明父类便于 getSubclassesOfAsync 异步发现 loader: () import(./OrderBlockModel), // 按需加载代码分割 }, }); } } // 3. 挂载到应用 const app new Application({ plugins: [PluginOrderBlock], });注册完成后OrderBlockModel就可以通过use: OrderBlockModel在任意模型树中被引用、创建与渲染并被引擎的异步子类查询如按CollectionBlockModel过滤区块自动发现。小结FlowModel 注册是 NocoBase 流程引擎开放扩展能力的入口继承合适的基类、实现render()、在插件load()中调用registerModelLoaders三步即可把自定义模型接入引擎。而registerModelLoaders提供的动态加载、extends继承声明、加载失败ErrorFlowModel兜底与预加载幂等机制则让这套注册体系在大规模插件化场景下既保持了首屏性能又具备了健壮的容错能力。理解这条注册 → 加载 → 解析 → 实例化的完整链路是深入使用 NocoBase 流程引擎扩展点区块、操作、事件的基础。【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表