扩展、用户脚本、浏览器自带能力的边界与分工
想给浏览器「加功能」,你面前其实有三条路:浏览器自带的能力、用户脚本、正式扩展。很多初学者的问题不是不会写,而是选错了武器——用扩展去干脚本一行元数据就能搞定的事,或者指望脚本去调用只有扩展才有的 API。这一篇把三者的边界讲清楚。
三条路,三种权力等级
| 方案 | 安装成本 | 能力上限 | 分发方式 |
|---|---|---|---|
| 浏览器自带能力 | 零 | 最低,仅限内置设置 | 无需分发 |
| 用户脚本 | 装一个管理器 | 中等,页面级改造 | 复制文本即可安装 |
| 正式扩展 | 需上架或侧载 | 最高,全浏览器级 | 商店或企业渠道 |
浏览器自带能力指 Chrome 设置面板里能调的东西:启动页、搜索引擎、字体、站点权限。它是「所有人的默认值」,颗粒度粗,改不了页面内容本身。
用户脚本要高一个层级。它依赖 Tampermonkey 这类脚本管理器,把一段带元数据头的 JavaScript 注入到匹配的网页里。它能改 DOM、改样式、发跨域请求,但只能活动在「页面」这个层面——管不了标签页、管不了浏览器 UI、拿不到 tabs 或 bookmarks 这类 API。
正式扩展是权力最大的形态。它有自己的后台(Service Worker)、自己的界面(popup、options、side panel),能注册右键菜单、响应快捷键、拦截网络请求、同步存储。代价是要写 manifest.json、走审核、维护版本。
判断标准:三个问题
动手前问自己三个问题,答案基本就能定方案:
- 要不要跨页面工作? 只在固定几个页面上改改内容,用户脚本足够;要监听全局标签页事件、管理下载、提供独立面板,必须扩展。
- 要不要用到特权 API?
chrome.tabs、chrome.storage.sync、chrome.notifications、chrome.commands这些命名空间,用户脚本一个都摸不到(除非借助 GM_* 模拟)。用得到,就上扩展。 - 要不要给陌生人用? 脚本发个文本文件、贴个链接就能分享;扩展需要打包、上架、过审。受众决定投入。
反过来说也成立:如果你的需求「一个 @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,不响应就是差评。
个人工具选脚本,产品化选扩展,这条经验背后就是维护责任的差异。团队里做技术选型时,「谁来长期维护」往往比「技术上能不能实现」更能决定方案。
从脚本迁移到扩展的信号清单
实际项目里,迁移往往是被具体事件触发的。出现以下任何一条,就认真考虑搬家:
- 脚本膨胀超过三百行,开始自己造「设置面板」的轮子;
- 用户开始问「能不能在手机上用」「能不能同步」;
- 你发现自己在脚本里往页面塞 UI,塞得越来越痛苦;
- 需求里出现了「定时」「后台」「监控」这类词。
此时迁移的路径是:先复用脚本的核心逻辑(它们与扩展的 content script 几乎同构),再补 manifest 与后台,最后把脚本的 GM API 调用翻译成对应的 chrome API。整个过程通常一两天。
本章小结
自带能力解决「设置」,用户脚本解决「页面」,扩展解决「浏览器」。判断标准不是技术难度而是能力边界:越界了就换更重的工具,没越界就别背着更重的工具跑。全书后面章节,就是把「扩展」这条最重的路,一段一段铺给你看。
三类需求的典型决策
用三个真实需求走一遍决策流程,感受判断标准怎么落地。
需求一:在某个文档站给代码块加行号。 单一站点、纯页面改造、无后台逻辑——用户脚本,五十行以内搞定,上扩展是浪费。
需求二:自动整理下载的发票 PDF 到指定目录。 涉及 downloads API、需要监听下载事件、与页面无关——必须扩展,脚本完全没有对应的 API。
需求三:团队内部使用的网页批注工具。 要存储批注数据、要用户界面、要给几十个同事用——扩展,而且要走企业分发或商店不公开渠道。
三个需求分别落在三条路上,判断依据不是「能不能实现」而是「哪条路实现成本与维护成本最低」。这个思路贯穿全书:工具没有高下,匹配需求才有价值。
混合策略的成本核算
双轨维护(脚本版 + 扩展版)不是免费的,启动前算一笔账:核心逻辑复用节省的开发时间约六成,但双端的 API 适配层、双端的测试、双端的用户沟通都是双倍。经验值是:当核心逻辑稳定(改动频率低于每月一次)且两个渠道的用户都不多时,双轨可行;任何一个渠道的用户量或迭代频率上来后,尽快收敛到单轨。
本章由 WorkBuddy 原创整理,API 行为依据 Chrome 扩展官方文档(Manifest V3)。
