构建可优先运行的MutationObserver包装器用于浏览器扩展
概述
出于安全考量,此MutationObserver包装器应能为开发者提供DOM中发生的所有变更信息。
问题
我需要一种简便的方法来创建MutationObserver的包装器,希望在页面中创建MutationObserver时收到通知,同时确保该代码优先于其他所有代码运行,以便将其集成到浏览器扩展中。
尝试代码
const dump = console.log.bind(console); class Observer { static started = false static jacked = {} static start() { Observer.started = true; Observer.hijack( MutationObserver, function constructor() { alert("CAREFUL!! - MutationObserver is running here!"); return (Observer.jacked.MutationObserver).constructor.apply(this, [...arguments]); }); // if (!Observer.fixes[document.domain]) // { // dump("not listining on: "+document.domain); // return; // }; // // Observer.fixes[document.domain]() } static hijack(target, impose) { let waiting = setInterval(()=> { if ((typeof target.prototype) !== "object"){ return }; if ((typeof target.prototype.constructor) !== "function"){ return }; clearInterval(waiting); let name = target.prototype.constructor.name; dump("hijacking: "+name); Observer.jacked[name] = target; Object.defineProperty(target.prototype, "constructor", {writable:false, configurable:false, enumerable:false, value:impose}); },0); } } let waiting = setInterval(() => { let exists = ((typeof MutationObserver) !== "undefined"); if ( !exists ) { console.log("observer waiting for `MutationObserver`"); return }; if ( Observer.started ) { clearInterval(waiting); console.log("observer already started .. exiting"); return }; clearInterval(waiting); Observer.start(); }, 0);
优化实现方案
你的核心思路没问题,但原代码的劫持方式不够可靠,轮询机制也没必要。针对浏览器扩展场景,要确保代码优先执行,重点是利用扩展的document_start加载时机,直接重写全局MutationObserver即可:
// 保存原始构造函数 const OriginalMO = window.MutationObserver; // 重写MutationObserver window.MutationObserver = function(callback) { // 触发创建通知 console.log("[监控] MutationObserver 被实例化"); // 包装原始回调,捕获所有DOM变更 const wrappedCallback = (mutations, observer) => { console.log("[监控] 捕获到DOM变更:", mutations); // 调用原始回调,保证原有逻辑正常运行 callback(mutations, observer); }; // 创建原始实例并返回 return new OriginalMO(wrappedCallback); }; // 同步原型方法,避免兼容性问题 Object.assign(window.MutationObserver.prototype, OriginalMO.prototype); // 修正构造函数指向 window.MutationObserver.prototype.constructor = window.MutationObserver;
关键注意事项
- 确保优先执行:在扩展的
manifest.json中,将content script的run_at设置为"document_start",这样代码会在页面所有脚本执行前完成重写:
{ "manifest_version": 3, "name": "DOM变更监控插件", "version": "1.0", "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"], "run_at": "document_start" } ] }
- 完整兼容:通过
Object.assign同步原始原型的所有方法,避免页面脚本调用observe、disconnect等方法时出错。 - 轻量高效:无需轮询等待
MutationObserver,document_start阶段window对象已初始化,可直接访问原始构造函数。 - 灵活扩展:在
wrappedCallback中可以自定义处理逻辑,比如过滤特定类型的变更、记录日志或上报数据等。
内容的提问来源于stack exchange,提问作者Lusi
相关产品推荐
相关产品推荐

