扩展、用户脚本、浏览器自带能力的边界与分工

想给浏览器「加功能」,你面前其实有三条路:浏览器自带的能力、用户脚本、正式扩展。很多初学者的问题不是不会写,而是选错了武器——用扩展去干脚本一行元数据就能搞定的事,或者指望脚本去调用只有扩展才有的 API。这一篇把三者的边界讲清楚。

三条路,三种权力等级

方案 安装成本 能力上限 分发方式
浏览器自带能力 零 最低,仅限内置设置 无需分发
用户脚本 装一个管理器 中等,页面级改造 复制文本即可安装
正式扩展 需上架或侧载 最高,全浏览器级 商店或企业渠道

浏览器自带能力指 Chrome 设置面板里能调的东西:启动页、搜索引擎、字体、站点权限。它是「所有人的默认值」,颗粒度粗,改不了页面内容本身。

用户脚本要高一个层级。它依赖 Tampermonkey 这类脚本管理器,把一段带元数据头的 JavaScript 注入到匹配的网页里。它能改 DOM、改样式、发跨域请求,但只能活动在「页面」这个层面——管不了标签页、管不了浏览器 UI、拿不到 tabs 或 bookmarks 这类 API。

正式扩展是权力最大的形态。它有自己的后台(Service Worker)、自己的界面(popup、options、side panel),能注册右键菜单、响应快捷键、拦截网络请求、同步存储。代价是要写 manifest.json、走审核、维护版本。

判断标准:三个问题

动手前问自己三个问题,答案基本就能定方案:

  1. 要不要跨页面工作? 只在固定几个页面上改改内容,用户脚本足够;要监听全局标签页事件、管理下载、提供独立面板,必须扩展。
  2. 要不要用到特权 API? chrome.tabs、chrome.storage.sync、chrome.notifications、chrome.commands 这些命名空间,用户脚本一个都摸不到(除非借助 GM_* 模拟)。用得到,就上扩展。
  3. 要不要给陌生人用? 脚本发个文本文件、贴个链接就能分享;扩展需要打包、上架、过审。受众决定投入。

反过来说也成立:如果你的需求「一个 @match 加五十行 JS」就能满足,写扩展纯属杀鸡用牛刀,还平白多出审核负担。

同一件事,两种实现

拿「把某网站背景改成深色」举例,用户脚本的写法:

// ==UserScript==
// @name        站点暗黑模式
// @match       https://example.com/*
// @grant       GM_addStyle
// ==/UserScript==
GM_addStyle(`
  body { background: #111 !important; color: #ddd; }
`);

正式扩展则要先写 manifest:

{
  "manifest_version": 3,
  "name": "站点暗黑模式",
  "version": "1.0",
  "content_scripts": [
    { "matches": ["https://example.com/*"], "css": ["dark.css"] }
  ]
}

再配套一个 dark.css。功能等价,但注意差异:扩展版把逻辑放进了纯 CSS,浏览器在页面渲染前就注入了样式,不会闪一下白底;脚本版要等 DOM 加载,深色生效前可能先闪一帧。这个「注入时机」的差异在改造类需求里经常被忽略,却是体验的分水岭,第三章讲 content scripts 时会展开。

混合策略是常态

成熟项目常常两条腿走路:核心功能做成扩展上架商店,同时维护一份用户脚本版给不方便安装扩展的场合(公司锁定了浏览器权限、或想快速灰度验证想法)。两者的正则与 URL 匹配规则高度相通,match 模式写法可参考本站《正则表达式从入门到精通》:正则实战,学一次两边受益。

编辑器选择上没有强制,VS Code、HBuilderX 都行。用 HBuilderX 的话,多文件工程的语法检查和项目管理很省心,技巧可参考 HBuilderX 效率技巧。

官方口径

Chrome 官方对分发的口径很明确:面向公众的扩展只能通过 Chrome Web Store 安装,自托管仅限企业管理环境。文档见 https://developer.chrome.com/docs/extensions/how-to/distribute 。而用户脚本没有官方通道,社区约定俗成靠 Greasy Fork、GitHub Gist 等渠道流通,这也是它「轻」的另一面——没人替你审核,装之前要自己看代码。

一句话总结分工:改页面找脚本,动浏览器用扩展,两者都不够再考虑原生应用。全书后面章节,就是把「扩展」这条最重的路,一段一段铺给你看。

三条路的能力明细

把上一节的粗表拆细,每个方案到底能碰什么、不能碰什么:

能力项 自带设置 用户脚本 正式扩展
修改页面 DOM / 样式 不能 能 能
跨站存储数据 不能 能(GM 存储) 能(chrome.storage)
跨域请求 不能 能(GM_xmlhttpRequest) 能(host 权限下的 fetch)
管理标签页 / 书签 / 下载 部分 不能 能
独立界面(弹窗/设置页/侧栏) 无 无 有
响应全局快捷键 无 无 能(commands)
拦截 / 改写网络请求 无 无 能(declarativeNetRequest)
自动更新与版本管理 系统 管理器提供 商店负责

从表里能读出一个重要结论:用户脚本与扩展的能力重叠区其实很大——改页面、跨域请求、存储两边都行。真正的分水岭在「页面之外」的浏览器级能力。这也解释了为什么很多老牌工具同时提供脚本版与扩展版:核心逻辑一份,壳按需换。

自带能力的进阶用法

别小看第一条路。Chrome 自带的 DevTools、性能面板、网络条件模拟覆盖了大量「改浏览器行为」的需求:强制深色模式(chrome://flags 里的 forced dark mode 实验特性)、自定义站点权限、翻译开关。很多用户装扩展只是因为不知道这些内置开关。给产品写文档时,把「系统自带能做的」与「需要扩展做的」分开说明,能显著降低支持成本。

同理,写扩展前先确认需求真的需要扩展。比如「屏蔽某网站通知」,站点权限设置里点两下就完成,写扩展纯属浪费。

选型的隐性变量:维护责任

三条路的维护成本差异巨大,做决定前把这笔账算进去:

  • 自带设置:零维护,浏览器负责;
  • 用户脚本:站点改版你就得修,但没有强制周期——脚本失效到用户反馈之间可以缓冲很久;
  • 扩展:Chrome 每四周一个稳定版,API 行为变化、审核政策变化都会变成你的维护任务;上架后用户带着期待提 issue,不响应就是差评。

个人工具选脚本,产品化选扩展,这条经验背后就是维护责任的差异。团队里做技术选型时,「谁来长期维护」往往比「技术上能不能实现」更能决定方案。

从脚本迁移到扩展的信号清单

实际项目里,迁移往往是被具体事件触发的。出现以下任何一条,就认真考虑搬家:

  1. 脚本膨胀超过三百行,开始自己造「设置面板」的轮子;
  2. 用户开始问「能不能在手机上用」「能不能同步」;
  3. 你发现自己在脚本里往页面塞 UI,塞得越来越痛苦;
  4. 需求里出现了「定时」「后台」「监控」这类词。

此时迁移的路径是:先复用脚本的核心逻辑(它们与扩展的 content script 几乎同构),再补 manifest 与后台,最后把脚本的 GM API 调用翻译成对应的 chrome API。整个过程通常一两天。

本章小结

自带能力解决「设置」,用户脚本解决「页面」,扩展解决「浏览器」。判断标准不是技术难度而是能力边界:越界了就换更重的工具,没越界就别背着更重的工具跑。全书后面章节,就是把「扩展」这条最重的路,一段一段铺给你看。

三类需求的典型决策

用三个真实需求走一遍决策流程,感受判断标准怎么落地。

需求一:在某个文档站给代码块加行号。 单一站点、纯页面改造、无后台逻辑——用户脚本,五十行以内搞定,上扩展是浪费。

需求二:自动整理下载的发票 PDF 到指定目录。 涉及 downloads API、需要监听下载事件、与页面无关——必须扩展,脚本完全没有对应的 API。

需求三:团队内部使用的网页批注工具。 要存储批注数据、要用户界面、要给几十个同事用——扩展,而且要走企业分发或商店不公开渠道。

三个需求分别落在三条路上,判断依据不是「能不能实现」而是「哪条路实现成本与维护成本最低」。这个思路贯穿全书:工具没有高下,匹配需求才有价值。

混合策略的成本核算

双轨维护(脚本版 + 扩展版)不是免费的,启动前算一笔账:核心逻辑复用节省的开发时间约六成,但双端的 API 适配层、双端的测试、双端的用户沟通都是双倍。经验值是:当核心逻辑稳定(改动频率低于每月一次)且两个渠道的用户都不多时,双轨可行;任何一个渠道的用户量或迭代频率上来后,尽快收敛到单轨。

本章由 WorkBuddy 原创整理,API 行为依据 Chrome 扩展官方文档(Manifest V3)。