方案背景
在刀耕火种的jQuery时代,我们都是对DOM直接进行操作,但往往也会面临一个问题:DOM被不明来源的操作修改了。而且这种改动大都不是顺着一条线的逻辑,而是一个犄角旮旯的事件监听,或是一次没有关键词的AOE操作,非常难排查。到了Vue时代,这种情况改善了很多,但如果有人乱写ref,或者随意的使用Watch,依然会碰到类似的问题。那么这种问题我们该怎么排查呢?
核心思路
浏览器其实有一个监听DOM发生任意变动的API:MutationObserver,不论是DOM上的属性,还是其内部的子元素,所有变动都可以监听到。不过不知为何这个东西并没有全面推广,Vue的$nextTick之前还使用该API充当过底层,但之后也替换成了Promise了,不过这倒是不影响咱们使用它
有了MutationObserver,我们就可以在目标DOM的变动事件中设置debugger,当然这个注册动作至少要早于未知操作,然后通过Chrome的Call Stack就能逐步找到操作的来源
具体实现
本方案思路是尽可能设计为外挂逻辑,操作都是在控制台而非代码中进行的
- 全局声明以MutationObserver为核心的debugger工具方法,Vue项目需要显示挂载到window上
// 针对找不到DOM是谁操作的情况专用debugger工具
// debugger卡住后可以从任务栈入手看上一层是谁导致的
function listenerMutationObserver (dom) {
const config = { attributes: true, childList: true, subtree: true };
// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
debugger;
};
// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);
// 以上述配置开始观察目标节点
observer.observe(dom, config);
}
// 全局挂载是为了咱们在控制台进行操作
window.listenerMutationObserver = listenerMutationObserver;
在一个尽可能早的节点将目标DOM传进来
a. 在Vue项目中可以选择mounted的第一行,在jQuery项目中可以选择DOM Ready回调的第一行
b. 如果不想在代码里修改,可以直接把断点打到如上的位置,然后在控制台里传入目标DOM// 控制台执行 window.listenerMutationObserver(document.getElementById('test'))
c. 如果目标DOM没有明显的抓取标志,那就在卡住断点后通过控制台动态给DOM设置一个
d. 如果不想在代码里写debugger,Sources里又找不到想卡断点的代码文件,那可以考虑通过发接口的来源入手
- debugger卡住不明DOM操作后,通过Call Stark确认可疑的帧
Vue的栈会比较多,各路Watch也因为在观察者队列中,很难直接观测,但至少可以找到变动的根源。如果是jQuery类项目,在Call Stack中会非常直观
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。