采用事件委托统一接管拖拽事件,通过画布容器监听dragstart/dragover/drop,结合data-comp-id精准识别组件,路由分发至中央调度器操作json schema,配合节流、批量更新与虚拟化渲染保障性能。

画布区组件数量多、交互密集时,直接给每个组件绑定拖拽事件会造成大量监听器冗余、内存占用高、事件响应卡顿。用事件委托统一接管,再按需分发,是稳定支撑上百组件的必要手段。
画布容器统管 drag 事件入口
不给每个组件单独加 draggable="true" 或 onDragStart,而是把整个画布区域(如 #canvas)设为唯一可拖拽源,并启用原生 dragstart 捕获能力:
- 在画布容器上设置
draggable="false"(防误触发),但允许其子元素发起拖拽;真正可拖拽的组件只在渲染时动态加draggable="true"属性 - 监听画布容器的
dragstart事件,利用e.target精准识别被拖拽组件的 DOM 节点和对应组件 ID(例如从data-comp-id属性读取) - 立即调用
e.dataTransfer.setData('text/plain', compId),把组件标识写入拖拽数据,供后续 drop 阶段使用
用事件委托替代遍历注册
避免循环 comList.forEach(c => c.el.addEventListener(...)),改用单层委托机制:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 所有组件渲染时统一加一个 class(如
comp-draggable)和唯一data-comp-id - 画布容器上监听
dragstart、dragover、drop三个事件,全部用事件捕获或冒泡阶段处理 - 在
dragstart中通过e.target.closest('.comp-draggable')定位源头组件,校验是否合法(比如是否禁用、是否嵌套在不可拖容器内) - 在
drop阶段,同样用e.target.closest('[data-drop-zone]')找落点区域,结合e.dataTransfer.getData('text/plain')获取原始组件 ID,完成路由分发
路由分发靠组件元数据 + 状态映射表
拖拽指令不是直接操作 DOM,而是转为对组件树(JSON Schema)的操作请求,由中央调度器分发:
- 维护一张轻量映射表:
const compRegistry = new Map<string type: string draggable: boolean parentpath:>()</string>,初始化渲染时填入 -
dragstart触发后,查表拿到该组件类型、是否支持自由拖拽、所属容器路径等上下文,决定是否允许本次拖拽 -
drop时,根据落点位置(如画布坐标、目标容器 ref)、当前组件类型(如容器类组件允许嵌套,原子组件只允许平级移动),调用对应 handler:移动、复制、插入子节点等 - 所有变更最终都作用于统一状态(如 pinia store 或 reactive schema 对象),触发画布重绘,而非手动 DOM 移动
性能兜底:节流 + 批量更新 + 虚拟化提示
面对上百组件,光靠委托还不够,得叠加运行时保护:
-
dragover默认每毫秒触发多次,必须用requestIdleCallback或简单节流(如 60ms 间隔)控制吸附计算频率 - 拖拽中不做实时 layout 更新,只记录临时偏移量;
dragend时批量提交一次 schema 变更,避免多次 re-render - 画布开启「拖拽模式」后,隐藏非关键组件的阴影/边框,用半透明占位符+网格线代替真实渲染,降低 GPU 压力










