ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

LogicFlow 事件系统完全指南:on/off/once/emit 方法、事件对象与全量事件清单实战详解

LogicFlow 事件系统完全指南:on/off/once/emit 方法、事件对象与全量事件清单实战详解 LogicFlow 事件系统完全指南on/off/once/emit 方法、事件对象与全量事件清单实战详解【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlowLogicFlow专注于业务自定义的流程图编辑框架内置了一套完善的事件系统用于将画布上发生的节点点击、边拖拽、连线调整、画布变换等各类交互实时告知开发者。本篇技术指南以lf实例提供的事件方法on、off、once、emit为主线完整梳理事件命名规范、回调参数结构以及节点、边、锚点、画布、History、选区、文本与各类插件事件的完整清单并结合packages/core中的源码实现与单元测试帮助你准确、高效地在自己的业务流程中订阅和响应画布变化。读完本文你将能够熟练监听任意事件、精确解除监听、按需触发自定义事件并读懂每个事件回调中携带的数据。事件系统概览一切交互皆事件当用户使用鼠标或其它方式与画布交互时LogicFlow 会触发对应的事件。通过监听这些事件可以获取其在触发时所产生的数据例如被点击节点的 id、鼠标位置、拖拽位移量等进而实现校验、埋点、联动、数据回写等业务功能。事件系统的核心是eventCenter——LogicFlow 内部的通讯中心。在源码中它是graphModel上的一个EventEmitter实例参见 GraphModel.ts 中readonly eventCenter: EventEmitter的定义并在构造器中完成初始化。lf实例上的on/off/once/emit四个方法本质上都是对这一eventCenter的代理转发见 LogicFlow.tsx。事件名遵循namespace:eventName的命名结构详见 event.md节点node、边edge、锚点anchor、画布空白区域blank、历史记录history、选区selection等 namespace 将同名的click、drag事件区分开使事件监听更精确、不易出错。在 constant/index.ts 中这些事件名被统一收敛为EventType枚举如NODE_CLICK node:click、EDGE_ADD edge:add保证内部抛出与外部监听的字符串完全一致。// 监听与触发示例lf 实例上的 API lf.on(node:click, (data) { /* ... */ })事件方法on / off / once / emiton监听事件lf.on(evt, callback)用于监听事件支持单个事件与多个事件用英文逗号分隔同时监听。参数名称类型必传默认值描述evtstring✅-事件名称支持用逗号分隔监听多个事件callbackEventCallback✅-事件回调函数示例// 监听单个事件 lf.on(node:click, (data) { console.log(节点被点击:, data); }); // 监听多个事件 lf.on(node:click,edge:click, (data) { console.log(元素被点击:, data); }); // 监听节点移动事件 lf.on(node:drag, ({ data }) { console.log(节点拖拽中:, data.id, data.x, data.y); }); // 监听画布点击事件 lf.on(blank:click, ({ e }) { console.log(画布被点击:, e.x, e.y); });注意事项事件名称支持用逗号分隔同时监听多个事件回调函数的参数格式取决于具体的事件类型重复监听同一事件会叠加都会被执行。从源码层面看on的实现会先将evt按逗号切分并trim再把{ callback, once }依次 push 进_events哈希表的对应事件数组中见 eventEmitter.ts。这意味着同一回调注册多次、以及用逗号注册多个事件都会产生多条监听记录全部会被触发。对于回调参数EventCallback的类型定义结合了 eventArgs.ts 中声明的EventArgs凡是内置事件名参数类型会被严格约束为对应的事件对象如果是未声明的自定义事件名则参数类型退化为any由用户自行保证数据结构。off取消事件监听lf.off(evt, callback)用于取消事件监听。参数名称类型必传默认值描述evtstring✅-事件名称支持用逗号分隔取消多个事件callbackEventCallback✅-要取消的事件回调函数示例// 定义回调函数 const handleNodeClick (data) { console.log(节点被点击:, data); }; // 监听事件 lf.on(node:click, handleNodeClick); // 取消监听 lf.off(node:click, handleNodeClick); // 取消多个事件监听 lf.off(node:click,edge:click, handleNodeClick);注意事项必须传入监听时使用的同一个回调函数引用如果回调函数引用不匹配取消操作无效支持同时取消多个事件的监听。源码中的off逻辑见 eventEmitter.ts还支持三种更灵活的清场方式evt为空字符串时清空所有事件的监听器this._events {}evt非空但callback为空时删除指定事件的全部监听器delete this._events[evt]evt与callback均非空时则通过events[i].callback callback的对象引用比较逐一移除匹配项并在数组清空后删除该事件键。因此若要移除用匿名函数注册的监听必须提前将回调保存为具名函数引用。once只触发一次的监听lf.once(evt, callback)与on类似但回调执行一次后会自动取消监听。参数名称类型必传默认值描述evtstring✅-事件名称支持用逗号分隔监听多个事件callbackEventCallback✅-事件回调函数示例// 只监听一次节点点击事件 lf.once(node:click, (data) { console.log(首次点击节点:, data); // 这个回调只会执行一次 }); // 监听画布首次渲染完成 lf.once(graph:rendered, (data) { console.log(画布渲染完成:, data); // 可以在这里执行一些初始化操作 });注意事项回调函数执行一次后会自动取消监听适用于只需要响应一次的场景如初始化操作支持同时监听多个事件但每个事件都只触发一次。在 eventEmitter.ts 中once实际是复用了on并传入once true。emit分发时会对once标记的监听在回调执行前先splice移除若该事件的监听数组因此为空还会删除整个事件键见 eventEmitter.ts。单元测试 event.test.ts 精确验证了这一行为同一回调经once注册后触发一次即被移除再次getEvents().test1结果为undefined。emit手动触发事件lf.emit(evt, eventArgs)用于手动触发事件。参数名称类型必传默认值描述evtstring✅-事件名称eventArgsEventArgs✅-事件参数示例// 触发自定义事件 lf.emit(custom:event, { type: custom:event, data: { message: Hello World, }, }); // 监听自定义事件 lf.on(custom:event, (data) { console.log(收到自定义事件:, data); }); // 触发节点相关事件谨慎使用 lf.emit(node:click, { type: node:click, data: lf.getNodeDataById(node_1), });注意事项主要用于触发自定义事件谨慎触发内置事件可能会影响 LogicFlow 的正常运行事件参数格式需要符合对应事件的规范触发的事件会被所有对应的监听器接收。emit的源码实现见 eventEmitter.ts还有两个值得注意的细节一是同样支持逗号分隔同时触发多个事件二是分发时除了执行对应事件名的监听器外还会额外执行*通配符监听器——这意味着你可以注册lf.on(*, ...)来观察所有事件的触发。此外graphModel自身在内部也大量通过eventCenter.emit(...)抛出事件例如节点删除NODE_DELETE、边添加EDGE_ADD、边删除EDGE_DELETE、画布尺寸变化graph:resize等见 GraphModel.ts。事件对象回调参数的通用结构不同事件回调携带的参数并不相同但多数事件对象由以下公共字段构成属性类型值dataObject事件涉及元素的数据属性节点事件或边数据属性边事件eMouseEvent原生的鼠标事件对象positionObject鼠标触发点在画布中的坐标参照 getPointByClient 的返回值msgstring边校验信息仅connection:not-allowed等校验类事件携带这些类型在 eventArgs.ts 中有完整的 TypeScript 声明。以node:click为例其事件对象为{ data, e, position }并额外携带isSelected点击后节点是否选中与isMultiple是否多选状态而node:resize则携带preData调整前的节点数据、data、model节点 model、deltaX/deltaY鼠标位移与index当前调整的控制点编号可在拖拽缩放控制点时用于精细的数据回写。建议在监听时直接解构出所需字段例如lf.on(node:click, ({ data, e, position }) {})代码更简洁且类型更清晰。节点事件事件名说明事件对象element:click元素单击data, e, positionnode:click节点单击data, e, positionnode:dbclick节点双击data, e, positionnode:mousedown鼠标按下节点data, enode:mouseup鼠标抬起节点data, enode:mousemove鼠标移动节点data, enode:mouseenter鼠标进入节点data, enode:mouseleave鼠标离开节点data, enode:delete节点的删除datanode:add节点的添加datanode:dnd-add外部拖入节点添加时触发datanode:dnd-drag外部拖入节点拖拽中触发data, enode:dragstart节点开始拖拽data, enode:drag节点拖拽data, enode:drop节点拖拽放开data, enode:contextmenu右键点击节点data, e, positionnode:resize2.0 新增调整节点缩放preData, data, model, deltaX, deltaY, indexnode:properties-change2.0 新增节点自定义属性变化id: 当前节点的 idkeys: 当前变更字段的 key 的集合preProperties: 改动前的 propertiesproperties: 改动后的 properties事件对象包含如下内容属性类型值dataObject节点的数据属性eMouseEvent原生的鼠标事件对象positionObject鼠标触发点在画布中的坐标参照 getPointByClient 的返回值其中node:properties-change是在节点自定义属性properties发生变更时触发的事件常用于实现节点属性联动。node:resize则依托NodeResize扩展提供的能力在拖拽节点的缩放控制点时实时回调最新的位置尺寸数据便于将变更同步到后端。边事件事件名说明事件对象element:click元素单击data, e, positionedge:click边单击data, e, positionedge:dbclick边双击data, eedge:mouseenter鼠标进入边data, eedge:mouseleave鼠标离开边data, eedge:add边增加dataedge:delete边删除dataedge:contextmenu边右键data, e, positionedge:adjust边拖拽调整dataedge:exchange-node调整边的起点/终点dataconnection:not-allowed不允许建立连接data, msg事件对象包含如下内容属性类型值dataObject边的数据属性eMouseEvent原生的鼠标事件对象positionObject鼠标触发点在画布中的坐标参照 getPointByClient 的返回值msgstring边校验信息edge:adjust在用户拖拽调整折线边路径时触发edge:exchange-node在调整边的起点/终点后触发事件对象中的data为{ newEdge, oldEdge }分别表示调整前后两端的边数据connection:not-allowed则在连线校验失败如目标节点不允许作为连接终点时触发回调中的msg可直接用于 toast 提示。锚点事件事件名说明事件对象anchor:dragstart锚点连线开始拖动data, e, nodeModelanchor:drop锚点连线拖动连线成功只有在创建连线成功的时候才触发。用于区分手动创建的连线和自动创建的连线edge:adddata, e, nodeModel, edgeModelanchor:drag锚点连线拖动触发data, e, nodeModelanchor:dragend锚点连线结束不管是否创建连线都会触发data, e, nodeModel事件对象包含如下内容属性类型值dataObject锚点数据eMouseEvent原生的鼠标事件对象nodeModelObject锚点所属的节点锚点事件用于感知从节点锚点拖出连线的完整过程anchor:dragstart表示连线开始拖出anchor:drop仅在连线成功创建时触发注意它与edge:add的区别——edge:add也覆盖了非拖拽方式创建的边可通过edgeModel拿到新创建的边模型anchor:dragend则无论是否创建成功都会触发适合作为收尾清理。画布事件事件名说明事件对象blank:mousedown画布鼠标按下eblank:mousemove画布鼠标移动eblank:mouseup画布鼠标抬起eblank:click画布单击eblank:contextmenu画布右键e, positionblank:dragstart画布开始拖拽eblank:drag画布拖拽eblank:drop画布拖拽放开etext:update文案更新datagraph:transform画布平移或者缩放触发datagraph:rendered画布渲染数据后触发即lf.render(graphData)方法被调用后触发。v1.1.0 新增graphDatagraph:updated画布重新更新后触发即lf.render(graphData)方法被调用后或者改变画布graphModel上的属性后触发。如果是主动修改某个属性导致画布更新想要在画布更新后做一些操作建议注册事件后在回调函数中及时注销该事件或者使用once事件代替on事件因为其他属性也可能导致画布更新触发该事件。v2.0.0 新增-事件对象包含如下内容属性类型值eMouseEvent原生的鼠标事件对象positionObject鼠标触发点在画布中的坐标参照 getPointByClient 的返回值在源码实现中graph:rendered由 LogicFlow.tsx 在渲染流程中通过this.emit(EventType.GRAPH_RENDERED, ...)抛出graph:resize则在画布容器尺寸变化时触发并为了性能做了 16ms 的防抖处理见 eventArgs.ts。需要特别注意的是graph:updated由于任意 graphModel 属性的变更都可能触发该事件因此官方明确建议在回调中及时注销监听或改用once以免收到非预期的多次回调。History 事件History 用来记录画布上的每一次改动当画布上的元素发生变化时会触发history:change事件。事件名说明事件对象history:change画布变化data事件对象中的 data 属性包含以下内容。属性类型说明undosArray可撤销的 graph 快照redosArray可重做的 graph 快照undoAbleboolean是否可以撤销redoAbleboolean是否可以重做利用该事件可以非常方便地实现工具栏上的撤销/重做按钮的可用状态联动每次画布变化后根据data.undoAble/data.redoAble实时刷新按钮的 disabled 状态。在核心源码中History 实例由new History(eventCenter)创建并挂载到事件系统上见 LogicFlow.tsx其完整实现可参考 history/index.ts。选区事件当同时选中多个节点形成选区时选区触发的事件。事件名说明事件对象selection:selected选区框选后触发所有选中的元素selection:mousedown选区鼠标按下eselection:dragstart选区开始拖拽eselection:drag选区拖拽eselection:drop选区拖拽放开eselection:mousemove选区鼠标移动e, positionselection:mouseup选区鼠标松开eselection:contextmenu选区右键e事件对象包含如下内容属性类型值eMouseEvent原生的鼠标事件对象positionObject鼠标触发点在画布中的坐标参照 getPointByClient 的返回值文本事件当 Text 文本位置、内容出现变更时文本触发的事件。事件名说明事件对象text:mousedown鼠标按下文本e, datatext:dragstart开始拖拽文本e, datatext:drag拖拽文本e, datatext:drop放开文本e, datatext:click单击文本etext:dbclick双击文本etext:blur文本失焦etext:mousemove鼠标在文本区内部移动e, data, deltaX, deltaYtext:mouseup鼠标在文本区内放开e, datatext:update更新文本data事件对象包含如下内容属性类型值eMouseEvent原生的鼠标事件对象dataObjectNodeModel/EdgeModel插件事件不同插件会抛出各自的命名空间事件监听方式与内置事件完全一致同样通过lf.on(...)订阅。DndPanel事件名说明事件对象dnd:panel-dbclick拖拽面板双击e, datadnd:panel-click拖拽面板左键单击e, datadnd:panel-contextmenu拖拽面板右键单击e, data事件对象包含如下内容属性类型值eMouseEvent原生的鼠标事件对象dataObjectNodeModel/EdgeModelMiniMap事件名说明事件对象miniMap:close小地图隐藏时触发-SelectionSelect事件名说明事件对象selection:selected-area选框范围topLeft: 左上角坐标, bottomRight: 右下角坐标selection:drop鼠标放开后如果存在框选选中的元素时触发eselection:selected框选完成时触发elements: 框选元素集合, topLeft: 左上角坐标, bottomRight: 右下角坐标DynamicGroup / Group事件名说明事件对象group:add-node节点加入到分组中触发data: 分组数据, childId: 新加入节点的 idgroup:remove-node节点从分组中移除触发data: 分组数据, childId: 移除节点的 idgroup:not-allowed命中节点不允许加入到分组中时触发group: 分组数据, node: 被禁止加入的节点的信息dynamicGroup:collapse2.1.0 新增分组节点折叠事件collapse: 折叠状态nodeModel: 节点实体分组Group / DynamicGroup插件的事件可用于实现把节点拖入分组/移出分组后的业务联动如权限校验提示、分组内统计更新其中group:not-allowed回调中的group与node字段可直接用于展示该节点不允许加入此分组的原因。Highlight事件名说明事件对象highlight:single单元素高亮模式下元素触发高亮datahighlight:neighbours相邻元素高亮模式下元素触发高亮data, relateElementshighlight:path路径元素高亮模式下元素触发高亮data, relateElements事件对象包含如下内容属性类型值dataObjectNodeModel/EdgeModelrelateElementsArrayNodeModel/EdgeModel 组成的数组Label事件名说明事件对象label:mousedown鼠标按下文本e, datalabel:dragstart开始拖拽文本e, datalabel:drag拖拽文本e, datalabel:drop放开文本e, datalabel:mousemove鼠标在文本区内部移动e, data, deltaX, deltaYlabel:mouseup鼠标在文本区内放开e, data事件对象包含如下内容属性类型值eMouseEvent原生的鼠标事件对象dataObjectNodeModel/EdgeModel进阶实践用 eventCenter 实现自定义事件除了lf实例上的监听接口LogicFlow 还提供了eventCenter对象即graphModel上的属性用于监听和触发事件这在自定义节点内部尤为有用。在自定义节点组件中你可以从props拿到graphModel再通过graphModel.eventCenter.emit(...)抛出带业务语义的自定义事件外层业务代码只需lf.on(custom:xxx, ...)即可订阅class ButtonNode extends HtmlNode { setHtml(rootEl) { const { properties } this.props.model const el document.createElement(div) el.className uml-wrapper const html div div classuml-headHead/div div classuml-body divbutton onclicksetData()/button ${properties.name}/div div${properties.body}/div /div /div el.innerHTML html rootEl.innerHTML rootEl.appendChild(el) window.setData () { const { graphModel, model } this.props graphModel.eventCenter.emit(custom:button-click, model) } } }关于eventCenter的完整属性说明见 graphModel 文档。内部抛出的每个事件都会携带一个事件对象作为回调入参可包含节点 id、边 id、原生鼠标事件对象等任意相关信息。另外需要留意的是如果组件内部监听了eventCenter事件在组件销毁时需要取消这些监听避免内存泄漏。最佳实践小结区分on与once只在初始化等一次性场景使用once如graph:rendered持续跟踪用户交互如node:click、node:drag用on。善用逗号批量注册lf.on(node:click,edge:click, handler)一次监听多类事件配合解构({ data })使用更简洁。精确解除监听off依赖回调引用匹配务必保存具名回调若想清空某事件全部监听可在off时省略回调参数源码支持。谨慎使用emit触发内置事件内置事件由引擎内部抛出手动触发可能破坏内部状态emit的正确定位是业务自定义事件。警惕graph:updated的多次触发任何 graphModel 属性变化都可能触发它在回调中及时注销或改用once。合理利用通配符监听注册lf.on(*, ...)可全局观察所有事件流适合调试与埋点。事件系统是打通 LogicFlow 与业务逻辑的桥梁。本文完整的事件名清单、事件对象字段与源码级实现eventEmitter.ts、eventArgs.ts、constant/index.ts、event.test.ts可作为你开发时的速查手册配合事件基础教程一起阅读效果更佳。【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表