执行时机:document-start
油猴脚本的执行时机由 @run-at 控制,document-start 是最早的时机,在浏览器开始解析 HTML 前执行:
// ==UserScript==
// @name Block Scripts
// @match *://*/*
// @run-at document-start
// ==/UserScript==
此时 document.body 还不存在,但 DOM API 已可用,可以提前 hook 原型方法。
方案1:hook appendChild 拦截动态 script
// ==UserScript==
// @name Block Dynamic Scripts
// @match *://example.com/*
// @run-at document-start
// ==/UserScript==
(function () {
const rawAppend = Element.prototype.appendChild;
Element.prototype.appendChild = function (el) {
if (el.tagName === 'SCRIPT') {
const src = el.src || '(inline)';
console.log('[blocked]', src);
// 返回 el 但不真正追加,阻止加载
return el;
}
return rawAppend.call(this, el);
};
})();
rawAppend 保存原始方法,避免无限递归。只拦截 SCRIPT 标签,其他元素正常追加。
方案2:hook createElement,在 src 赋值时拦截
部分页面通过 createElement('script') 创建后再设置 src:
const rawCreate = document.createElement.bind(document);
document.createElement = function (tag) {
const el = rawCreate(tag);
if (tag === 'script') {
Object.defineProperty(el, 'src', {
configurable: true,
set(v) {
if (v.includes('ads.example.com')) {
console.log('[blocked src]', v);
return;
}
el.setAttribute('src', v);
},
get() {
return el.getAttribute('src') || '';
}
});
}
return el;
};
Object.defineProperty 劫持 src 的 setter,在 src 被赋值时决定是否放行。
方案3:MutationObserver 监控已插入的 script
上面两种方案针对动态插入。对于 HTML 中静态的 <script> 标签,可以用 MutationObserver 在插入前禁用:
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.tagName === 'SCRIPT' && node.src.includes('tracker.js')) {
node.type = 'text/blocked'; // 改掉 type,浏览器不执行
console.log('[neutralized]', node.src);
}
}
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
将 type 改为非 JavaScript MIME 类型,浏览器不会执行该脚本但 DOM 中仍有该节点。
三种方案对比
| 方案 | 拦截时机 | 适用场景 |
|---|---|---|
| hook appendChild | 追加时 | 动态 document.body.appendChild(script) |
| hook createElement src | src 赋值时 | el.src = '...' 模式 |
| MutationObserver | 节点插入 DOM 后 | 任意方式插入,包括 innerHTML |
实际页面可能混合使用多种方式,建议组合 hook。
注意事项
document-start时document.body为 null,不要访问 DOM 元素- hook 原型方法影响所有调用,测试时先在控制台验证
- 浏览器扩展的 Content Script 比油猴更底层,如需更强的拦截能力可考虑开发扩展
