NocoBase 插件开发选型指南:Component 与 FlowModel 的能力边界与生命周期对比
【免费下载链接】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 插件开发中,前端 UI 的编写存在两条路径:普通 React 组件与FlowModel。本文以 Component vs FlowModel 为骨架,结合 FlowEngine 源码与配套文档,讲清两者"谁替代谁"的关系、能力差异、生命周期映射以及渐进式采用的实战策略。读完你将能准确判断"什么时候写 React 组件、什么时候用 FlowModel 包装",并理解可视化配置能力在底层是如何由模型树驱动的。
核心判断:一个问题的选型标准
两者不是互相替代的关系——FlowModel 是在 React 组件之上的一层封装,它给组件加上了可视化配置的能力。在动手写代码之前,只需要问自己一个问题:
这个组件需要出现在 NocoBase 的「添加区块 / 字段 / 操作」菜单里,让用户在界面上进行可视化配置吗?
- 不需要→ 用普通 React 组件,就是标准的 React 开发;
- 需要→ 用 FlowModel 包装。
这个判断标准贯穿整个插件开发过程,也是 NocoBase 对"组件是否可配置"这一边界问题的官方答案。
默认方案:普通 React 组件
大部分插件场景用普通 React 组件就够了,例如:
- 注册一个独立页面(插件设置页、自定义路由页面);
- 写一个弹窗、表单、列表等内部组件;
- 封装一个工具类 UI 组件。
这些场景下,直接用 React + Antd 写组件,通过useFlowContext()拿到 NocoBase 的上下文能力(发请求、国际化、路由导航、日志等),跟普通前端开发没有区别:
import { useFlowContext } from '@nocobase/flow-engine'; export default function MySettingsPage() { const ctx = useFlowContext(); return ( <div> <h2>{ctx.t('Plugin settings')}</h2> {/* 普通 React 组件,不需要 FlowModel */} </div> ); }普通组件的完整能力来源
组件开发文档 Component 组件开发 给出了更完整的落地姿势,核心有三点:
- 路由挂载的页面组件就是普通 React 组件。在插件的
load()里通过this.router.add()注册路由即可挂载到 URL(见 Router 路由)。 - 状态管理推荐
observable+observer,而非useState。二者都从@nocobase/flow-engine导入:用observable.deep()创建响应式对象,用observer()包裹组件。好处是直接修改对象属性即可触发更新(无需setState)、自动依赖收集(组件只在用到的属性变化时重渲染),并且与 NocoBase 底层(FlowModel、FlowContext)的响应式机制保持一致:
import React from 'react'; import { Input } from 'antd'; import { observable, observer } from '@nocobase/flow-engine'; const state = observable.deep({ text: '', }); const DemoPage = observer(() => { return ( <div> <Input placeholder="输入点什么..." value={state.text} onChange={(e) => { state.text = e.target.value; }} /> {state.text && <div style={{ marginTop: 8 }}>你输入了:{state.text}</div>} </div> ); }); export default DemoPage;useFlowContext()是连接 NocoBase 能力的入口,返回ctx对象,常用能力包括:
import { useFlowContext } from '@nocobase/flow-engine'; export default function MyPage() { const ctx = useFlowContext(); // ctx.api — 发请求 // ctx.t — 国际化 // ctx.router — 路由导航 // ctx.logger — 日志 }- 发请求:
ctx.api.request({ url: 'users:list', method: 'get' }),用法与 Axios 一致; - 国际化:
ctx.t('Save success', { ns: '@my-project/plugin-hello' }),可指定命名空间; - 路由导航:
ctx.router.navigate('/some-page'),并通过ctx.route.params/ctx.route.name读取当前路由参数与路由名。
完整能力清单见 Context 上下文。
什么时候用 FlowModel
当组件满足以下三个条件时,就该用 FlowModel:
- 出现在菜单里:需要让用户通过「添加区块」「添加字段」「添加操作」菜单来添加;
- 支持可视化配置:用户可以在界面上点击配置项来修改组件的属性(比如修改标题、切换显示模式);
- 配置需要持久化:用户的配置需要保存下来,下次打开页面时还在。
简单来说,FlowModel 解决的是"让组件可配置、可持久化"的问题。如果你的组件不需要这些能力,就不需要用它。
从源码看,FlowEngine(流引擎)正是驱动 NocoBase 界面上区块、字段、操作按钮渲染、配置面板与配置持久化的核心引擎,见 FlowEngine 概述。对插件开发者而言,FlowEngine 提供两个核心概念:
- FlowModel— 可配置的组件模型,负责渲染 UI 和管理 props;
- Flow— 配置流程,定义组件的配置面板和数据处理逻辑。
二者的关系:不是替代,而是包装
FlowModel 不是用来"替代" React 组件的,它是在 React 组件之上的一层抽象:
React 组件:负责渲染 UI ↓ 包装 FlowModel:管理 props 来源、配置面板、配置持久化一个 FlowModel 的render()(或其子类重写的renderComponent())方法里,写的就是普通的 React 代码。区别在于:普通组件的 props 是写死的或从父组件传入的,FlowModel 的 props 是通过 Flow(配置流程)动态生成的。
实际上,两者在基本结构上很相似:
// React 组件 class MyComponent extends React.Component { render() { return <div>Hello</div>; } } // FlowModel class HelloModel extends FlowModel { render() { return <div>Hello</div>; } }组件树 vs 模型树
不过它们的管理方式完全不同:
- React 组件靠 JSX 嵌套形成组件树——这是运行时的 UI 渲染树;
- FlowModel则由 FlowEngine 管理,形成模型树——一棵可持久化、可动态注册的逻辑结构树,通过
setSubModel/addSubModel显式控制父子关系,适合构建页面区块、操作流、数据模型这类需要配置化管理的结构。
在源码中,flowModel.tsx 的addSubModel会通过flowEngine.createModel创建子模型并设置parentId、subKey,将子模型挂到parent.subModels[subKey]数组,同时向模型自身事件总线与引擎级事件总线(model:subModel:added)发射变更事件;而setSubModel则用于设置单一(非数组)的子模型。模型树上的父子关系由此被显式、可持久化地管理起来,这正是 FlowModel 与 React 组件树最本质的差异。
能力对比:一张表看清差异
从更技术的角度看二者的差异:
| 能力 | React 组件 | FlowModel |
|---|---|---|
| 渲染 UI | render() | render() |
| 状态管理 | 内建state/setState | 通过props和模型树结构管理 |
| 生命周期 | constructor、componentDidMount、componentWillUnmount | onInit、onMount、onUnmount |
| 响应输入变化 | componentDidUpdate | onBeforeAutoFlows、onAfterAutoFlows |
| 错误处理 | componentDidCatch | onAutoFlowsError |
| 子组件 | JSX 嵌套 | setSubModel/addSubModel显式设置子模型 |
| 动态行为 | 事件绑定、状态更新 | 注册和派发 Flow |
| 持久化 | 无内建机制 | model.save()等,和后端打通 |
| 多实例复用 | 需手动处理 | createFork——比如表格的每一行 |
| 引擎管理 | 无 | 由 FlowEngine 统一注册、加载、管理 |
生命周期映射
如果你熟悉 React 的生命周期,FlowModel 的生命周期很容易映射过来:
onInit对应constructor;onMount对应componentDidMount;onUnmount对应componentWillUnmount。
这些钩子在源码中均有完整实现与测试佐证:onInit定义于 flowModel.tsx(由构造函数在属性定义完成后调度调用,子类可覆盖并super.onInit(options));onMount/onUnmount定义于同一文件,且由FlowModelRenderer在挂载/卸载时调用,测试用例见 flowModel.test.ts,覆盖了"作为渲染目标"与"作为子模型"两种调用场景;onInit的父子调用顺序(父先于子)则在 flow-model-oninit.test.ts 中有明确断言。
FlowModel 的独有能力
另外,FlowModel 还提供了一些 React 组件没有的能力:
registerFlow— 注册 Flow,定义配置流程;applyFlow/dispatchEvent— 执行或触发 Flow;openFlowSettings— 打开 Flow 步骤的设置面板;save/saveStepParams()— 持久化模型配置;createFork— 一个模型逻辑被复用渲染多次(比如表格每行)。
这些能力全部在 flowModel.tsx 中有真实实现:registerFlow同时提供静态与实例两种形态(L637、L681);applyFlow/dispatchEvent经由flowEngine.executor执行(L852、L901);createFork用于生成逻辑复用的 fork 模型(L1384),其多实例语义在 forkFlowModel.ts 中实现;save与saveStepParams负责配置持久化(L1451、L1458);openFlowSettings负责打开 Flow 步骤的设置面板(L1658)。
这些能力是支撑「可视化配置」体验的基础。如果你的场景不涉及可视化配置,不需要关心它们。完整参考见 FlowEngine 完整文档。
场景对照:什么场景选什么方案
| 场景 | 方案 | 原因 |
|---|---|---|
| 插件设置页 | React 组件 | 独立页面,不需要出现在配置菜单里 |
| 工具类弹窗 | React 组件 | 内部组件,不需要可视化配置 |
| 自定义数据表格区块 | FlowModel | 需要出现在「添加区块」菜单,用户可以配置数据源 |
| 自定义字段展示组件 | FlowModel | 需要出现在字段配置里,用户可以选择展示方式 |
| 自定义操作按钮 | FlowModel | 需要出现在「添加操作」菜单里 |
| 封装一个图表组件给区块用 | React 组件 | 图表本身是内部组件,由 FlowModel 的区块来调用它 |
FlowModel 实战速览:三步走 + 基类选择
为了让选型结论落到实处,这里结合 FlowEngine 概述 与 区块扩展,给出 FlowModel 从创建到注册的完整路径。
1. 继承基类,实现 renderComponent
// models/HelloBlockModel.tsx import React from 'react'; import { BlockModel } from '@nocobase/client-v2'; import { tExpr } from '@nocobase/flow-engine'; export class HelloBlockModel extends BlockModel { renderComponent() { return ( <div> <h3>Hello FlowEngine!</h3> <p>这是一个自定义区块。</p> </div> ); } } // define() 设置菜单里的显示名 HelloBlockModel.define({ label: tExpr('Hello block'), });renderComponent()就是这个模型的渲染方法,类似 React 组件的render()。tExpr()用于延迟翻译——因为define()在模块加载时就执行了,此时 i18n 还没初始化。
2. 在 Plugin 里注册
// plugin.tsx import { Plugin } from '@nocobase/client-v2'; export class MyPlugin extends Plugin { async load() { this.flowEngine.registerModelLoaders({ HelloBlockModel: { // 按需加载,首次用到时才加载模块 loader: () => import('./models/HelloBlockModel'), }, }); } }3. 用 registerFlow 添加配置项
光能渲染还不够——FlowModel 的核心价值在于可配置。通过registerFlow()可以给模型添加配置面板,让用户在界面上修改属性:
SimpleBlockModel.registerFlow({ key: 'flow1', title: tExpr('Simple Block Flow'), on: 'beforeRender', // 渲染前执行 steps: { editHtml: { title: tExpr('Edit HTML Content'), // uiSchema 定义配置面板的 UI uiSchema: { html: { type: 'string', title: tExpr('HTML Content'), 'x-decorator': 'FormItem', 'x-component': 'Input.TextArea', }, }, // 默认值 defaultParams: { html: `<h3>This is a simple block</h3> <p>You can edit the HTML content.</p>`, }, // handler 里把配置面板的值设置到 model 的 props 上 handler(ctx, params) { ctx.model.props.html = params.html; }, }, }, });关键点解读:
on: 'beforeRender'— 表示这个 Flow 在渲染前执行,配置面板的值会在渲染前写入this.props;uiSchema— 用 JSON Schema 格式定义配置面板的 UI(语法参考 UI Schema),常用组件包括Input、Input.TextArea、Select(配合enum)、Switch等,每个字段用'x-decorator': 'FormItem'包裹即可自动带上标题和布局;handler(ctx, params)—params是用户在配置面板填写的值,通过ctx.model.props设置到模型上;defaultParams— 配置面板的默认值。
4. 基类选择
NocoBase 提供了多个 FlowModel 基类,根据要扩展的类型选择:
| 基类 | 用途 | 详细文档 |
|---|---|---|
BlockModel | 普通区块 | 区块扩展 |
DataBlockModel | 需要自行获取数据的区块 | 区块扩展 |
CollectionBlockModel | 绑定数据表、自动获取数据 | 区块扩展 |
TableBlockModel | 完整表格区块,自带字段列、操作栏等 | 区块扩展 |
FieldModel | 字段组件 | 字段扩展 |
ActionModel | 操作按钮 | 操作扩展 |
继承链路为BlockModel→DataBlockModel→CollectionBlockModel→TableBlockModel。通常来说,做表格区块用TableBlockModel(最常用、开箱即用),需要完全自定义渲染用CollectionBlockModel或BlockModel,做字段用FieldModel,做操作按钮用ActionModel。
渐进式采用:先 React 后 FlowModel
不确定的时候,先用 React 组件实现功能。等确认需要可视化配置能力后,再用 FlowModel 包装——这是推荐的渐进式做法。大块内容用 FlowModel 管理,内部细节用 React 组件实现,两者配合使用。
这一策略在实践中非常有效:FlowModel 的renderComponent()内部完全可以渲染普通 React 组件(如图表库封装),而 FlowModel 只负责对外暴露配置面板与持久化。这样既保留了配置化管理的收益,又避免了为纯内部组件引入不必要的复杂度。
相关文档索引
- Component 组件开发 — React 组件的写法、
observable/observer状态管理、useFlowContext用法; - FlowEngine 概述 — FlowModel 基础用法、
renderComponent、registerFlow、uiSchema配置与基类选择; - 区块扩展 —
BlockModel系列基类与自定义区块注册; - FlowEngine 完整文档 — FlowModel、Flow、Context 的完整参考;
- Context 上下文 —
useFlowContext的完整能力介绍。
【免费下载链接】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),仅供参考