MutationObserver 防篡改的核心是监控画布容器、识别非平台变更并毫秒还原:仅监听 #canvas-root 等语义化锚点,启用 childList/subtree/关键 attributes 观察,回调中三层判定非法操作,修复时先断开再还原最后重连。

用 MutationObserver 给低代码平台画布加防篡改锁,核心不是“锁死 DOM”,而是盯住画布容器、识别非平台自身发起的变更、并安全还原——它不阻止篡改发生,但能让非法拖拽、脚本注入、控制台误操作在毫秒内被识别和拦截。
只锁定画布根容器,避开组件树噪音
低代码平台的画布通常有稳定锚点,比如 #canvas-root、[data-role="design-canvas"] 或 .lc-canvas。不要监听 document.body 或整个 #app,否则会混入组件预览更新、属性面板联动、实时协作同步等合法变更,导致误报率飙升。
- 优先用
document.querySelector('#canvas-root')获取目标,若画布异步加载,可配合requestIdleCallback延迟初始化 - 避免依赖动态 class(如
canvas-123abc),用语义化data-属性或固定 ID 锚定 - 若平台支持多画布(如页签式设计区),需为每个画布单独实例化 observer,不共用配置
配置精简观察项,覆盖典型篡改动作
画布篡改集中在三类行为:拖入非法组件(script/iframe)、删除关键节点(如根容器、绑定锚点)、修改样式隐藏控件(display: none、z-index 覆盖)。只需启用以下组合:
-
childList: true—— 捕获控制台执行canvasRoot.appendChild(adDiv)或插件注入悬浮窗 -
subtree: true—— 确保子组件内部改动也被捕获(例如在按钮组件内插入伪造span) -
attributes: true + attributeFilter: ['class', 'style', 'data-lc-id', 'hidden']—— 只盯影响渲染与绑定的关键属性,跳过id、title等无关变更 - 禁用
characterData—— 画布不承载法律文本,无需监听文本节点;禁用attributeOldValue—— 不需比对旧值,省开销
回调中做三层轻量判定,不盲目响应
每次回调收到的是批量记录,里面可能夹杂平台自身触发的更新(如组件重渲染)。必须快速过滤出真正可疑的操作:
- 第一层归属判断:
if (!canvasRoot.contains(mutation.target) && mutation.target !== canvasRoot) return - 第二层类型筛选:若
type === 'childList',遍历addedNodes,用node.matches('script, iframe, [id^="ad-"], [class*="taboola"]')匹配非法节点 - 第三层意图识别:若
type === 'attributes'且attributeName === 'style',检查getComputedStyle(target).display === 'none'或zIndex异常(如> 9999且非平台设定值)
安全修复:断开 → 还原 → 重连
一旦确认篡改,修复动作必须隔离、可逆、不递归:
- 先调用
observer.disconnect(),防止target.remove()或el.style.display = ''再次触发回调 - 优先还原原始状态:若画布初始化时已缓存
canvasRoot.dataset.originalHTML,可用canvasRoot.innerHTML = originalHTML;若只改样式,用el.style.removeProperty('display')替代全量重写 - 修复完成后,再调用
observer.observe(canvasRoot, config)恢复监控 - 对高频场景(如用户频繁拖拽组件),加简单节流:记录上一次修复时间戳,50ms 内同类变更直接忽略

















