uBlock Origin 静态过滤引擎如何用 serialize 序列化缓存避免重复解析过滤列表
【免费下载链接】uBlockuBlock Origin - An efficient blocker for Chromium and Firefox. Fast and lean.项目地址: https://gitcode.com/GitHub_Trending/ub/uBlock
在 Node.js 环境中使用 uBlock Origin 的静态网络过滤引擎(SNFE,Static Network Filtering Engine)时,每次启动都要从网络拉取 EasyList 等过滤列表,再经过解析、编译成内部结构,这个过程既慢又依赖远端可用。@gorhill/ubo-core包提供的serialize()/deserialize()接口可以把解析好的引擎内容存成一个 JS 字符串,之后再启动时直接反序列化载入,README 中对此的说明是 "fast-load the content of the static network filtering engine without having to parse and compile the lists"(快速载入引擎内容,无需重新解析和编译列表)。本文基于仓库中 npm 包文档、demo.js 和引擎源码 static-net-filtering.js,给出可执行的缓存操作路径与验证方式。
serialize 到底缓存了什么
在 static-net-filtering.js 中,serialize()直接委托给toSelfie()(源码注释称缓存为 "selfie")。toSelfie()先对内部结构做优化(this.optimize(0)、bidiTrie.optimize()、origHNTrieContainer.optimize()),然后返回一个包含以下字段的状态对象(见 toSelfie 实现):
version:序列化格式版本,当前引擎内部为selfieVersion = '10'(第 4483 行);processedFilterCount、acceptedCount、discardedCount、bitsToBucket:过滤统计与哈希桶参数;urlTokenizer、destHNTrieContainer、origHNTrieContainer、bidiTrie:URL 分词器与各主机名压缩 trie 容器(HNTrieContainer 是 SNFE 的关键组件,专门用于高效存储和查找大量主机名);filterData、filterRefs:已编译的过滤规则数据及其引用表。
反序列化路径fromSelfie()(第 5212-5243 行)先reset()引擎并设置notReady = true,逐项恢复上述组件;其中有一个关键检查:如果selfie.version !== this.selfieVersion则直接返回false,此时内部状态不可信,调用方必须自行重置引擎并重新灌入过滤列表(unserialize()只是fromSelfie()的别名)。
准备条件
根据 package.json 的 engines 字段,环境要求为:
- Node.js
>= 18.0.0 - npm
>= 6.14.4
npm 包使用原生 JavaScript modules(ES module 语法)。README 提示这是很早期的版本,API 随时可能变化。
执行步骤
以下是最短可行路径,直接复用包内自带的 demo.js(文档原文给出的启动方式):
mkdir myproject cd myproject npm install @gorhill/ubo-core cp node_modules/@gorhill/ubo-core/demo.js . node demo.js由于 demo 使用 ES module 语法,文档建议在生成的package.json中添加"type": "module"以消除警告。
第一次运行时 demo 会依次做三件事(见 demo.js 第 50-88 行):
- 检查本地
cache/selfie.txt是否存在且文件修改时间不超过 7 天(ageInDays <= 7); - 缓存可用时,读取该文件并执行
await snfe.deserialize(selfie),跳过网络拉取; - 缓存不可用时,通过
snfe.useLists([...])拉取 uAssets CDN 的filters.min.txt、badware.min.txt、privacy.min.txt、unbreak.min.txt、quick-fixes.min.txt,以及 easylist.to 的easylist.txt、easyprivacy.txt和 PGL 的 hosts 列表,然后执行const selfie = await snfe.serialize(),将结果写入cache/selfie.txt。
如果你要在自己的代码中使用同一套接口,README 给出的最小调用序列是:
import { StaticNetFilteringEngine } from '@gorhill/ubo-core'; const snfe = await StaticNetFilteringEngine.create(); // 首次:灌入过滤列表(如何获取列表由你自己决定) await snfe.useLists([ fetch('easylist').then(r => r.text()).then(raw => ({ name: 'easylist', raw })), ]); // 序列化引擎内容,得到一个 JS 字符串,可存盘 const serializedData = await snfe.serialize(); // 后续启动:直接用该字符串载入,无需解析和编译列表 const snfe2 = await StaticNetFilteringEngine.create(); await snfe2.deserialize(serializedData);注意 README 说明:目前同一时间只能存在一个 SNFE 实例。
验证缓存是否生效
验证手段全部来自 demo.js 自身的可观察行为:
首次运行(无缓存或缓存超过 7 天):终端会为每个列表打印fetched日志(如easylist fetched),运行结束后cache/selfie.txt被创建。随后 demo 对三个测试请求执行snfe.matchRequest(test)并打印结果:返回1打印Blocked:及snfe.toLogData(),返回2打印Unblocked:及toLogData(),其余打印Not blocked。README 的示例中,https://securepubads.g.doubleclick.net/tag/js/gpt.js这类请求被判为 Blocked,而页面自身的that.css判为 Not blocked——这些是文档给出的示例结果,实际判定取决于你灌入的列表内容。
7 天内再次运行:cache/selfie.txt未过期时,demo 直接走deserialize分支,终端不再出现各列表的fetched日志和Fetching lists...日志,匹配测试照常输出。文档对这一行为的说明是 "The serialized data will be reused if available in order to avoid fetching from remote server each time it is executed"。这就是"避免重复解析"的直接验证:网络拉取与解析编译步骤被整体跳过。
缓存过期:当selfie.txt的文件修改时间距现在超过 7 天(demo 中按7 * 24 * 60 * 60秒计算),demo 会重新拉取列表并覆盖写入新的序列化数据。
缓存失效的两个边界
- 文件格式版本:
fromSelfie()会校验selfie.version与当前引擎的selfieVersion(当前为'10'),不一致时返回false并让引擎停留在notReady状态。此时必须放弃该缓存、重置引擎并重新走useLists()解析路径。 - 单实例限制:README 明确当前只能有一个 SNFE 实例,序列化/反序列化用于跨进程或跨次启动复用状态,而不是在同一进程内并行维护多套引擎。
参考
- npm 包文档与 API 说明:platform/npm/README.md
- 可直接运行的缓存 demo:platform/npm/demo.js
- 引擎序列化/反序列化实现:src/js/static-net-filtering.js
- 独立使用 HNTrieContainer 主机名 trie 的说明,见 platform/npm/README.md 的 Extras 一节。
【免费下载链接】uBlockuBlock Origin - An efficient blocker for Chromium and Firefox. Fast and lean.项目地址: https://gitcode.com/GitHub_Trending/ub/uBlock
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考