 事件订阅指南:字段双向绑定、资源刷新监听与监听器生命周期管理)
NocoBase RunJS ctx.on() 事件订阅指南字段双向绑定、资源刷新监听与监听器生命周期管理【免费下载链接】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/nocobaseRunJS 是 NocoBase 中用于JS 区块JSBlock、JS 字段JSField / JSEditableField、JS 操作JSAction等场景的 JavaScript 执行环境代码运行在受限沙箱中并通过统一的ctx上下文访问页面能力参见 RunJS 概述。ctx.on()是其中负责订阅上下文事件的核心 API通过它可以在字段值被外部修改时同步更新 UI、响应资源刷新/保存等生命周期事件从而把自定义 JS 代码无缝接入表单联动、数据刷新等 NocoBase 既有机制。读完本文你将掌握ctx.on()的类型定义、事件分发规则、与ctx.off()的配对清理模式以及js-field:value-change、resource:refresh等事件的底层实现与完整实战写法。ctx.on() 的定位与适用场景在 RunJS 脚本中ctx.on()用于订阅上下文事件——例如字段值变化、属性变化、资源刷新等。事件会根据名称前缀被映射到两种不同的通道以resource:为前缀的事件 → 走ctx.resource的内部事件总线其余事件 → 通常映射为ctx.element上的自定义 DOM 事件CustomEvent。ctx.on()的典型适用场景如下场景说明JSField / JSEditableField监听字段值从外部表单、联动等变更时同步更新 UI实现双向绑定JSBlock / JSItem / JSColumn监听容器上的自定义事件响应数据或状态变化resource 相关监听资源刷新、保存等生命周期事件在数据更新后执行逻辑类型定义on(eventName: string, handler: (event?: any) void): void;eventName事件名称字符串。以resource:开头的名称走资源事件总线其余名称走ctx.element上的 DOM 事件若容器存在。handler事件回调。DOM 事件通道下参数为事件对象新值通过ev.detail携带。常见事件一览事件名说明事件来源js-field:value-change字段值被外部修改如表单联动、默认值更新ctx.element上的 CustomEventev.detail为新值resource:refresh资源数据已刷新ctx.resource事件总线resource:saved资源保存完成ctx.resource事件总线事件最终映射规则以resource:为前缀的走ctx.resource.on其余通常走ctx.element上的 DOM 事件若存在。实战示例字段双向绑定React useEffect 清理在 JS 字段中渲染自定义 UI 时通常需要把本地 React state 与表单字段值保持同步。以下代码在挂载时订阅js-field:value-change并在卸载时通过 cleanup 移除监听React.useEffect(() { const handler (ev) setValue(ev?.detail ?? ); ctx.on?.(js-field:value-change, handler); return () { ctx.off?.(js-field:value-change, handler); }; }, []);这里ev.detail即外部写入的新字段值回调中把它同步到本地setValue实现外部值变化 → UI 更新的单向数据流。配合ctx.setValue/ctx.getValue参见 ctx.setValue()即可构成完整的双向绑定。原生 DOM 监听ctx.on 不可用时的替代不同 context 类型提供的能力不同ctx.on可能不存在。此时可直接使用ctx.element的原生addEventListener// 当 ctx.on 未提供时可直接使用 ctx.element const handler (ev) { if (selectEl) selectEl.value String(ev?.detail ?? ); }; ctx.element?.addEventListener(js-field:value-change, handler); // 清理时ctx.element?.removeEventListener(js-field:value-change, handler);资源刷新后更新 UI对于依赖资源数据的场景可以订阅ctx.resource的事件总线在数据刷新后重新读取数据并渲染ctx.resource?.on(refresh, () { const data ctx.resource?.getData?.(); // 根据 data 更新渲染 });从源码看这一模式在 NocoBase 内置模型中被广泛使用。例如 JSItemActionModel.tsx 中即存在resource.on(refresh, handler)与resource.off(refresh, handler)的配对订阅写法可作为自定义脚本的参照实现。源码级原理js-field:value-change 是如何产生的ctx.on之所以能监听到字段值变化是因为渲染层在值变更时向容器派发了标准的 DOM CustomEvent。以可编辑 JS 字段为例其实现位于 JSEditableFieldModel.tsx事件派发在JSFormRuntime组件中useEffect监听value变化并派发事件useEffect(() { if (!containerRef.current || !scriptCode) return; const event new CustomEvent(js-field:value-change, { detail: value }); containerRef.current.dispatchEvent(event); }, [value, scriptCode]);这里detail携带的正是最新字段值与文档中ev.detail为新值的约定完全对应见 JSEditableFieldModel.tsx。容器与 element 暴露脚本真正运行前runJs步骤的 handler 通过ctx.onRefReady等待容器引用就绪再以ctx.defineProperty(element, ...)暴露容器——且返回值包裹在ElementProxy中带 XSS 保护并通过动态 getter 绑定ref.current避免容器变更后失效见 JSEditableFieldModel.tsx。默认模板即使用该事件JSEditableFieldModel的默认脚本模板本身就演示了监听js-field:value-change→ 同步本地 state →onChange时回写ctx.setValue的完整闭环见 JSEditableFieldModel.tsxReact.useEffect(() { const handler (ev) setValue(ev?.detail ?? ); ctx.element?.addEventListener(js-field:value-change, handler); return () ctx.element?.removeEventListener(js-field:value-change, handler); }, []);测试验证该行为有对应的单元测试覆盖见 JSEditableFieldModel.test.tsx 中的EDITABLE_CODE用例——它直接以ctx.element?.addEventListener(js-field:value-change, handler)作为被测脚本验证外部值变化能够驱动 UI 同步。只读字段JSFieldModel中的事件语义对于只读形态的 JS 字段如表格列、详情项模型实现位于 JSFieldModel.tsx它渲染一个占位span容器当props.value变化时通过useHooksBeforeRender重新执行jsSettings脚本刷新内容见 JSFieldModel.tsx。由于容器在只读场景同样存在js-field:value-change的 DOM 监听模式依旧可用而ctx.setValue这类写能力则仅在带表单绑定的上下文中提供。与 ctx.off 的配合ctx.on注册的监听必须成对管理具体规则参见 ctx.off()使用ctx.on注册的监听应在适当时机通过ctx.off移除避免内存泄漏或重复触发在 React 中通常在useEffect的 cleanup 函数中调用ctx.offctx.off可能不存在使用时建议加可选链ctx.off?.(eventName, handler)。资源事件总线同样遵循这一模式const handler () { /* ... */ }; ctx.resource?.on(refresh, handler); // 适当时机 ctx.resource?.off(refresh, handler);注意事项配对取消每次ctx.on(eventName, handler)都应有对应的ctx.off(eventName, handler)且传入的handler引用必须一致——不同的函数引用无法正确移除监听。生命周期在组件卸载或 context 销毁前移除监听否则可能导致内存泄漏。从源码结构看NocoBase 的字段模型在挂载时会做首次执行的幂等保护如JSFieldModel中的_mountedOnce标记见 JSFieldModel.tsx但自定义脚本中的监听清理责任仍在脚本作者一方。事件可用性不同 context 类型支持的事件不同具体以各组件文档为准ctx.on缺失时请退回到ctx.element的原生addEventListener。可写能力按需判断js-field:value-change只负责通知值变化写入操作应通过ctx.setValue?.(value)完成且仅在带表单绑定的上下文中可用详见 ctx.setValue() 的注意事项。相关文档ctx.off() - 移除事件监听ctx.element - 渲染容器与 DOM 事件ctx.resource - 资源实例及其on/offctx.setValue() - 设置字段值会触发js-field:value-change【免费下载链接】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),仅供参考