按 Enter 键跳转到正文

Chrome 插件开发

从一个真实问题开始

我经常在浏览器里看技术文章。看到好的想存到 Obsidian,复制粘贴格式全乱。打开"另存为"保存下来是带广告、导航栏、侧边栏的 HTML。想找一个干净的 Markdown 版本,要么没有,要么是闭源插件不敢用。

于是我开始想:我能不能自己写一个?

这是个典型的"想动手"时刻。但真正动手之前,有几个更前置的问题需要想清楚:

  • Chrome 插件到底是什么?和 userscript、网页工具、PWA 差在哪?
  • 我想做的这个事,到底该用哪种形态?
  • 学 Chrome 插件的"投资回报"如何?是一次性投入还是持续折腾?

写这篇文章的目的,是在你打开 manifest.json 之前,帮你想清楚这三个问题。

插件是什么

先把"插件"这个词拆开看。

Chrome 插件(Chrome Extension),也叫浏览器扩展,本质上是一段运行在 Chrome 浏览器里的代码。它能访问一些普通网页拿不到的 API,比如读取当前标签页的 URL、修改请求头、在用户点击工具栏图标时弹出一个小页面。

可以把它理解为:

插件 = 对浏览器的扩展能力 + 对已有网站的增强能力

注意两个关键词:“对浏览器”“对已有网站”。这两个词决定了插件能做什么、不能做什么。

对浏览器意味着插件能拿到浏览器层面的能力,比如:

  • 操作标签页(关闭、新建、切换)
  • 读取和修改浏览器历史
  • 拦截和修改网络请求
  • 在工具栏添加图标
  • 监听键盘快捷键

对已有网站意味着插件的核心战场是"改造现有网站",比如:

  • 给维基百科加一个深色模式
  • 给 GitHub 加一个一键复制文件链接的按钮
  • 给掘金、知乎、CSDN 屏蔽某些干扰元素

插件能做什么、不能做什么

能做的:

  • 改造已有网站(修改 DOM、注入脚本)
  • 提供一个常驻工具栏入口(点击图标触发动作)
  • 跨页面保存数据(用户的偏好、笔记、历史)
  • 监听浏览器事件(标签页变化、下载事件)

不能做(或做起来很费劲):

  • 完全独立的产品(需要服务端配合的复杂功能)
  • 移动端体验(Chrome 插件不适用于移动浏览器)
  • 高性能要求的事(不适合跑复杂的计算任务)
  • 跨浏览器一致体验(Firefox、Edge、Safari 各有差异)

什么时候该用插件

给几个真实场景,帮你判断。

适合用插件的场景:

  1. 改造已有网站:给某个你不喜欢的网站加功能或去干扰
  2. 跨页面工具:复制当前页面内容、抓取数据、自动填表
  3. 浏览器层的小工具:标签页管理、下载管理、截图、翻译
  4. 给开发者自己用:调试、API 测试、页面抓取

不适合用插件的场景:

  1. 需要登录、要用户数据、要后端服务:你应该做个完整的 Web 应用
  2. 需要跨设备同步、要复杂的用户系统:插件不是产品形态的完整答案
  3. 想要完整的移动端体验:Chrome 插件在手机上几乎用不上
  4. 想做一个完整的 SaaS 产品:插件只是入口,不是产品

一个简单的判断标准:

如果你的核心场景是"我在浏览某个网站时想多做点什么",用插件。

如果你的核心场景是"用户打开我的产品完成某件事",用 Web 应用。

插件和其他形态的对比

把插件和几种常见的形态放一起看。

形态优点缺点适合什么
Chrome 插件触发快、分发简单、有浏览器 API平台限制 Chrome 生态、需要上架审核改造已有网站、浏览器层小工具
Userscript(油猴脚本)写起来简单、不用上架分发依赖 Greasy Fork、不能调浏览器 API纯改页面的轻量需求
网页小工具跨平台、跨浏览器每次要打开新页面、不能主动操作当前页面数据展示、计算、查询
PWA能离线、能装到桌面浏览器 API 能力受限、用户认知度低阅读类、内容类工具
桌面应用(Electron 等)能力强、跨平台包大、分发麻烦、用户安装成本高复杂客户端、离线优先
书签(Bookmarklet)一行 JS 就能写、零分发成本能力极弱、不能存数据极简工具

一个具体例子:把当前页面存成 Markdown

  • 插件:点工具栏图标,触发下载,几秒钟搞定。✅ 适合
  • Userscript:能改页面但分发靠 Greasy Fork,朋友想用要去搜。⚠️ 凑合
  • 网页工具:复制 URL 粘贴到新页面处理。❌ 太麻烦
  • 桌面应用:包太大。❌ 杀鸡用牛刀
  • 书签:完全做不了。❌ 能力不够

一个反例:做个团队协作笔记

  • 插件:能做入口,但需要后端、需要用户系统、需要数据同步。⚠️ 勉强
  • Web 应用:自然形态,用户打开就能用。✅ 适合

记住这个判断方法:

先看"我做的事情发生在哪个上下文里"。

发生在浏览网页时 → 插件 / userscript。

发生在打开我的产品时 → Web 应用。

需要长期运行、复杂计算 → 桌面应用。

一个插件的最小构成

一个 Chrome 插件由几个独立的"部分"组成。每个部分在不同的上下文里运行,之间通过消息通信协作。

 1┌─────────────────────────────────────────┐
 2│              工具栏图标                   │
 3│  点击 → 弹出 popup.html                  │
 4└──────────────┬──────────────────────────┘
 5               │ sendMessage
 6 7┌─────────────────────────────────────────┐
 8│         content script                  │
 9│  注入到目标网页,能读/改页面 DOM          │
10└──────────────┬──────────────────────────┘
11               │ 异步通信
1213┌─────────────────────────────────────────┐
14│      background (service worker)        │
15│  后台运行,处理跨页面的逻辑              │
16│  存数据、调浏览器 API                   │
17└─────────────────────────────────────────┘

manifest.json:插件的身份证

每个插件必须有一个 manifest.json 文件。这是插件的元信息,Chrome 靠它认识你的插件:叫什么、用什么权限、能做什么。

content script:注入到目标页面

content script 是注入到目标网页里的 JavaScript。它能读取和修改页面的 DOM,但运行在一个"隔离的世界"里,不能访问页面的 JavaScript 变量。

这是插件最核心的能力:你能用代码改造任何网站。

popup:点击图标弹出的页面

popup 是用户点击工具栏图标时弹出的一个 HTML 页面。本质上就是一个普通网页,只是尺寸小、生命周期短。

background (service worker):后台常驻逻辑

V3 时代,background 不再是常驻页面,而是一个 service worker。它在需要时被唤起,处理完逻辑后会休眠。

不要把大量状态存在 service worker 里。它不是常驻进程。

options page:用户配置(可选)

如果你的插件有用户偏好,可以加一个 options 页面。用户在 chrome://extensions 里点"选项"进入。

icons:图标

插件必须有图标。商店要求至少 16/32/48/128 像素各一张。哪怕是占位图也要有。

这几部分怎么协作?

一个典型的数据流:

  1. 用户点击工具栏图标 → 弹出 popup
  2. popup 按钮触发 → 给 content script 发消息
  3. content script 提取页面内容 → 传给 popup
  4. popup 把内容通过 downloads API 触发下载
  5. 用户偏好(如选择器、文件名格式)存到 chrome.storage

这三层(popup / content script / background)之间的通信是插件开发的核心难点。比 API 本身重要得多。

Manifest V3 必须知道的几件事

如果你看过一些老教程,会发现很多代码是跑不通的。原因是 Chrome 在 2020 年宣布了 Manifest V3,V2 已经在 2024 年完全停用。

V3 的几个关键变化:

1. background 变成 service worker

V2 时代 background 是一个常驻页面(persistent: true)。V3 改成 service worker,逻辑执行完就休眠,不能依赖它常驻。

这意味着:

  • 不能在 background 里存全局变量
  • 不能依赖 background 一直运行
  • 需要长连接、长时间任务要换思路

2. 拦截请求用 declarativeNetRequest,不用 webRequest

V2 时代可以用 webRequest API 拦截和修改请求。V3 把这个能力收紧了,只能用 declarativeNetRequest。

差别:

  • webRequest:能读请求、能改请求、能阻塞
  • declarativeNetRequest:只能基于规则匹配和重定向,不能动态改请求体

对你的影响:

  • 如果你的插件只改页面 DOM,不拦截请求,这两种 API 跟你无关
  • 如果你的插件要做广告拦截、请求改写,要按 V3 规则重写

3. host_permissions 显式声明

V2 时代权限写在 permissions 里就行。V3 把"哪些网站能访问"拆出来放在 host_permissions。

1{
2  "permissions": ["storage", "downloads"],
3  "host_permissions": ["https://*.example.com/*"]
4}

4. 远程代码执行被禁止

V2 时代可以从 CDN 加载脚本。V3 不允许,所有代码必须打包进插件里。

要不要学 V2?

不用。

  • V2 已停用,新插件审核不通过
  • 老教程里 V2 的 API 命名、概念在 V3 里都有对应,但写法不同
  • 直接学 V3 就行,不要走弯路

上架一个插件的成本和周期

写完插件只是第一步。要让别人也能用,需要发到 Chrome Web Store。

成本

5 美元一次性注册费。需要 Visa 卡,注册过程需要科学上网(Google 服务在国内的访问问题)。

时间

材料准备大概 1-2 小时:

  • 插件代码(zip 打包)
  • 4 个尺寸的图标(16/32/48/128)
  • 商店图标(128x128,必须)
  • 商店截图(1280x800 或 640x400,至少 1 张)
  • 简短描述(最多 132 字符)
  • 详细描述
  • 隐私声明 URL(哪怕只是个 GitHub Pages 页面)

审核周期

首次提交 1-3 天。被打回再提交通常 1 天内。

最常见的打回原因:

  1. 缺少 privacy policy URL
  2. 权限申请超出实际需要
  3. 插件描述不清楚 single purpose(一个插件做太多事)
  4. icon 用了 Google 商标或禁用素材

如果只是给自己用:

不用上架。打开 chrome://extensions,开开发者模式,“加载已解压的扩展程序"选你的文件夹就行。代码改完点插件详情页的刷新按钮。

下一步

如果你看完还是想做 Chrome 插件,先回答两个问题:

  1. 你的核心场景发生在浏览网页时吗? 如果是,继续。
  2. 你的核心场景需要后端服务吗? 如果不需要,插件是合适形态。

满足这两个条件,就值得动手。

关于"具体怎么做”,看 《Chrome 插件开发实战:一键把当前页面存为 Markdown》。从 manifest.json 写到 Chrome Web Store 审核通过,一个具体例子走完整流程。

如果你只是想"给自己用"或"在团队里用",加载已解压的扩展就够了,不用走商店。

写在最后

Chrome 插件的入门成本比想象的高,但天花板比想象的低。

  • 一周内可以做一个能解决具体问题的小工具
  • 几乎所有能力都受限于 Chrome 提供的 API
  • 真要做好,需要和 Chrome 的限制周旋

但反过来想:

  • 一次开发,Chrome 用户都能用
  • 不用考虑跨平台
  • 不用考虑安装包的更新
  • 不用考虑应用商店的审核(Edge 商店审核宽松得多)

这是一件"小而美"的事。适合做出来给自己用,做出来给团队用,做出来给社区用。

发表评论