跳转到主要内容
用 declarativeNetRequest API 构建一个 adblocker(也写作 ad blocker 或 content blocker)。你声明匹配规则,浏览器在请求离开网络栈之前原生地拦截它们。

你要构建什么

为什么 MV3 用它取代了阻塞式 webRequest

Manifest V2 的广告拦截器注册阻塞式 webRequest 监听器。每个请求都要暂停,等扩展的 JavaScript 决定它的去留。出于性能和隐私考虑,Manifest V3 移除了这种模型。使用 declarativeNetRequest 时,浏览器自己求值你的规则。你的代码从不站在请求路径上,也从不为了拦截而读取请求内容。 对于代理类的用例,比如把所有流量路由到另一台服务器,请改用 proxy API 而不是请求规则。

Manifest

拦截和放行规则不需要 host 权限。重定向规则和 header 规则需要受影响站点的 host 权限。
manifest.json
Extension.js 会编译 rule_resources 引用的规则集文件,并把它随构建输出一起生成。

静态规则

静态规则放在 rules.json 中,随扩展加载而加载。
rules.json
||domain^ 过滤器语法匹配一个域名及其全部子域名。

拦截整个页面

在 resourceTypes 里写上 main_frame 的规则会拦截顶层导航,而不只是子资源:
rules.json
两个浏览器的表现不同,但都表示规则生效了:
  • Chrome 会把标签页替换成它自己的拦截页,显示 “This page has been blocked by an extension” 和代码 ERR_BLOCKED_BY_CLIENT。控制台里没有任何输出。
  • Firefox 会取消这次导航,标签页保留之前的页面。没有错误页,也没有控制台条目。
Extension.js 会把规则文件复制到 dist/<browser>/declarative_net_request/ 并改写 manifest 里的路径,所以按上面 manifest 的写法引用它即可。
本节和下一节的行为是在 Chrome 155 与 Firefox 158 上、使用 Extension.js 4.1.33 测得的。

把被拦截的页面重定向到扩展内的页面

把 block 动作换成 redirect,指向随扩展一起发布的页面:
rules.json
把页面放在 public/blocked.html。Extension.js 会把 public/ 复制到输出根目录,所以 /blocked.html 无需在 manifest 里登记就能解析。 重定向需要的权限比拦截多,而且两个浏览器不一样:
  • Chrome 需要覆盖该站点的主机权限。没有它,规则就是静默的空操作:页面正常加载,规则集仍显示已启用,控制台里什么都没有。对于指向扩展页面的 main_frame 重定向,Chrome 不需要 web_accessible_resources。
  • Firefox 既需要主机权限,也需要把页面列进 web_accessible_resources。缺少 web_accessible_resources 条目时,导航会以 NS_ERROR_DOM_BAD_URI 失败,标签页停留在原处。缺少主机权限时则和 Chrome 一样是静默的空操作。
action 模板把 Firefox 构建为 Manifest V2,那里的主机权限写在 permissions 中,web_accessible_resources 是字符串列表。带前缀的键会在其目标上替换掉同名的普通键,所以 Firefox 的列表要再写一次 declarativeNetRequest。借助浏览器专属字段,一份 manifest 同时覆盖两个浏览器:
manifest.json
如果你的项目也把 Firefox 构建为 Manifest V3,去掉 firefox: 键,在普通的 web_accessible_resources 下使用对象形式 [{ "resources": ["blocked.html"], "matches": ["<all_urls>"] }]。两个浏览器都接受。

动态规则

把动态规则用于运行时会变化的过滤器,比如用户手动添加的拦截清单条目。重新添加之前先移除对应的规则 id,让更新保持幂等。
background.js
Chrome 对静态与动态规则的数量设有上限。在发布大型过滤器清单之前,请在 declarativeNetRequest 参考文档中确认当前的限制。

在徽章上统计被拦截的请求

一次调用就能把 action 徽章变成每个标签页的规则匹配计数器。
background.js
要用 getMatchedRules 自己读取匹配到的规则,请添加 declarativeNetRequestFeedback 权限。

运行

在编辑 manifest.json 之前,请注意 manifest 变更需要重启开发服务器。见开发期更新行为。
打开一个会请求被拦截域名的页面。随着规则匹配,徽章计数会上升。

Firefox 说明

  • Firefox 在 Manifest V3 中支持 declarativeNetRequest。
  • Firefox 不支持 setExtensionActionOptions,所以徽章计数器只在 Chromium 上可用。
  • Firefox 在 Manifest V3 中仍然允许阻塞式 webRequest,而 Chrome 把它保留给策略安装的扩展。
  • 当两个目标出现分歧时,使用浏览器特定的 manifest 字段。

从模板开始

action 模板自带一个后台脚本和一个工具栏 popup,是拦截器 UI 的好起点。
代码仓库:extension-js/examples/action

最佳实践

  • 静态规则放在 rules.json 里,动态规则留给用户的选择。
  • 给每条动态规则一个稳定的 id,让移除保持可预期。
  • 把 resourceTypes 收窄到你实际拦截的类型,而不是所有类型。
  • 优先使用拦截和放行规则,它们不需要 host 权限。
  • 发布过滤器更新之前,先在真实页面上测试规则。

下一步