Chrome 插件开发入门

你能用插件做什么

丰富的 Chrome 插件能显著提升效率,比如广告屏蔽、长截图、网页收藏等。大多数插件的核心原理可以概括为一句话:

把 JavaScript 注入到网页里,对页面进行处理(改样式、加 UI、提取信息、自动化操作等),再配合插件自身的 UI 与后台能力,完成闭环。

插件由哪些部分组成

Chrome 插件通常由以下几部分组成(除 manifest.json 外其余都可选,按需求取用):

  • manifest.json:插件的"入口与权限说明",包含名称、版本、图标、页面入口、脚本声明、权限等
  • Service Worker(后台脚本):可以调用更多扩展 API,适合做消息中转、统一网络请求、任务调度、统一存储等(MV3 下不常驻)
  • 功能页面(扩展自己的页面):
    • Popup:点击工具栏图标弹出的页面
    • Options:插件配置页
  • Content Script(内容脚本):注入到目标网页的脚本,可操作 DOM,但与网页自身脚本相互隔离,能调用的扩展 API 也有限

你可以把它们理解成 3 类"进程/页面":

csharp 复制代码
用户点击
  |
  v
[Popup / Options]  <--- 扩展自己的页面(有独立 DOM)
  |
  |  消息(runtime.sendMessage / tabs.sendMessage)
  v
[Service Worker]   <--- 后台中枢(事件驱动,非长驻)
  |
  |  注入 / 消息
  v
[Content Script]   <--- 运行在目标网页(能改 DOM)

运行环境差异

如果你只记住两点,会少走很多弯路:

  1. Popup/Options 不能直接操作目标网页 DOM 它们是扩展自己的页面,跟目标网页是两套 DOM。

  2. 真正"改网页"的代码应该放在 Content Script Content Script 运行在目标网页里,天然适合做 DOM 操作与网页增强。

因此常见模式是:

  • Popup 负责 UI 与触发动作
  • Content Script 负责对当前页面做实际处理
  • Service Worker 负责更"系统级"的能力(统一请求、存储、调度、跨页面协作)

manifest.json:重点字段与读法

manifest.json 相当于插件的"声明式配置"。入门阶段重点关注这些字段:

  • manifest_version:现在都用 3
  • name / version / description:基本信息
  • action:工具栏图标与 popup
  • permissions:扩展 API 权限(例如 storagescripting 等)
  • host_permissions:网站访问范围(尽量最小化)
  • content_scripts:声明式注入内容脚本
  • background.service_worker:后台入口
  • options_page:配置页入口

一个"结构示意"式的 MV3 manifest 片段如下:

json 复制代码
{
  "manifest_version": 3,
  "name": "Your Extension",
  "version": "1.0.0",
  "action": { "default_popup": "popup.html" },
  "background": {
    "service_worker": "sw.js",
    "type": "module"
  },
  "options_page": "options.html",
  "permissions": ["storage", "scripting", "alarms"],
  "host_permissions": ["*://example.com/*"],
  "content_scripts": [
    {
      "matches": ["*://example.com/*"],
      "run_at": "document_idle",
      "js": ["content.js"],
      "css": ["style.css"]
    }
  ],
  "web_accessible_resources": [
    {
      "resources": ["logo.png", "inject.html"],
      "matches": ["*://example.com/*"]
    }
  ]
}

(清单字段、示例与约束):developer.chrome.com/docs/extens...

Content Script 注入:声明式 vs 程序式

内容脚本的注入常见有两种方式,可以组合使用:

1)声明式注入(manifest 里写 content_scripts

特点:

  • 优点:稳定,浏览器按规则自动注入
  • 缺点:如果用户在安装插件前已经打开了一堆网页,这些已打开页面通常不会自动注入,需要刷新或重新打开页面才生效

关键字段含义:

  • matches:哪些 URL 才注入
  • run_at:注入时机(常用 document_idle,减少对页面加载的影响)

2)程序式注入(用户触发时动态注入)

特点:

  • 优点:不用要求用户刷新页面;可以"按需注入",权限更可控
  • 缺点:需要处理"重复注入"与"注入时机"问题;MV3 推荐用 chrome.scripting.executeScript

需要注意的坑:

  • 动态注入如果每次点击都注一次,可能会导致重复执行带来的副作用(例如重复绑定事件、重复插入 UI)
  • 可以在 content script 的作用域内做"是否已初始化"的标记,避免重复初始化(标记建议挂在页面侧或脚本侧可控位置,避免污染页面逻辑)

三者如何通信

插件开发的"工程感"大多来自通信:Popup、Content Script、Service Worker 之间需要消息传递。

你可以把消息理解为前端里的"事件总线 + RPC"的简化版:

  • Popup → Content Script:对当前页面发指令
  • Content Script → Service Worker:请求后台能力(存储、网络、跨页面协作)
  • Service Worker → Popup:推送状态或响应结果(必要时)

消息的形态建议遵循两个原则:

  • 有明确的 type(最好是 namespace:action 形式,避免冲突)
  • 有明确的请求/响应结构(避免用"随便塞一个对象"的方式扩展到后期无法维护)

示意(仅展示结构,不作为完整例子):

js 复制代码
// request
{ type: 'devtools:requestList', payload: { ... } }

// response
{ ok: true, data: { ... } }

权限与安全

权限是插件的"安全边界",也是上架审核与用户信任的关键。

建议:

  • 能不用 "<all_urls>" 就不用,优先指定域名或路径
  • 能用 activeTab 的场景不要扩大 host 权限
  • 不要记录或上报敏感信息(URL query、cookie、token、响应体默认都应视为敏感)
  • 权限变化要在文档/更新日志里说清楚"为什么需要"

常见权限分工理解:

  • permissions:你要调用哪些扩展 API(比如 storagescripting
  • host_permissions:你要访问哪些站点(尽量最小化) (permissions / host_permissions / optional_*):developer.chrome.com/docs/extens...

调试指南:你应该打开哪一个 DevTools

"我看不到 console"是入门阶段最常见的问题。按运行环境分别打开:

  • Popup:在弹窗里右键 → 检查(Inspect)
  • Content Script:在目标网页按 F12 → Console/Sources
  • Service Worker:chrome://extensions/ → 找到扩展 → Service Worker 的 Inspect

定位顺序建议:

  1. 先确认 manifest 是否生效(扩展是否加载成功、权限是否正确)
  2. 再确认 content script 是否注入(在目标网页 Console 看是否有日志/断点)
  3. 再确认消息是否到达(发送/接收两端分别打印)
  4. 最后再看是否是权限/注入时机/重复执行导致的问题

常见坑

  • MV3 后台不常驻:不要把关键状态只放内存,需要持久化用 chrome.storage
  • 已打开页面没注入:声明式注入下,安装/更新后往往要刷新页面才生效
  • 重复注入/重复初始化:动态注入时要做好"只初始化一次"的机制
  • Content Script 与页面脚本隔离:访问不到网页脚本里定义的变量/函数(别用"我在页面 console 能跑"为标准)
  • 网络请求同源/权限问题:需要弄清请求发生在 content script 还是后台,以及对应的权限策略

实战演练:写一个"网页背景色修改器"

光说不练假把式。我们来写一个最小闭环的 MV3 插件:Focus Mode功能:点击插件图标,弹窗选择背景色,点击按钮将当前网页背景变色,并记住这个颜色。

1. 目录结构

创建一个文件夹 my-focus-extension,放入以下 4 个文件:

text 复制代码
my-focus-extension/
├── manifest.json
├── popup.html
├── popup.js
└── content.js

2. manifest.json (核心配置)

json 复制代码
{
  "manifest_version": 3,
  "name": "Focus Mode",
  "version": "1.0",
  "description": "Change page background color",
  "action": {
    "default_popup": "popup.html"
  },
  "permissions": ["storage", "activeTab"],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}

注意:这里为了演示方便用了 <all_urls>,实际发布时请尽量缩小范围。

3. popup.html (界面)

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <style>
    body {
      width: 200px;
      padding: 10px;
      font-family: sans-serif;
    }

    button {
      width: 100%;
      margin-top: 10px;
      padding: 5px;
      cursor: pointer;
    }
  </style>
</head>

<body>
  <h3>选择背景色</h3>
  <!-- 颜色选择器 -->
  <input type="color" id="colorPicker" value="#f0f0f0" style="width: 100%;">
  <button id="btnApply">应用背景色</button>
  <script src="popup.js"></script>
</body>

</html>

4. popup.js

js 复制代码
document.addEventListener('DOMContentLoaded', async () => {
  const btn = document.getElementById('btnApply')
  const picker = document.getElementById('colorPicker')

  // 1. 回显上次保存的颜色 (Storage API)
  // MV3 支持 Promise,不需要回调地狱
  const data = await chrome.storage.sync.get('focusColor')
  if (data.focusColor) {
    picker.value = data.focusColor
  }

  btn.addEventListener('click', async () => {
    const color = picker.value

    // 2. 保存颜色
    await chrome.storage.sync.set({ focusColor: color })

    // 3. 获取当前标签页
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })

    // 4. 发送消息给 Content Script
    if (tab.id) {
      // 这里的 type 建议加上命名空间防止冲突
      chrome.tabs.sendMessage(tab.id, {
        type: 'FOCUS_MODE:CHANGE_COLOR',
        color
      })
    }
  })
})

5. content.js (网页操作)

js 复制代码
// 监听来自 Popup 的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.type === 'FOCUS_MODE:CHANGE_COLOR') {
    document.body.style.backgroundColor = request.color
    console.log('背景色已修改为:', request.color)

    // 可选:给发送方回执
    sendResponse({ status: 'done' })
  }
})

总结

入门 Chrome 插件,不要先写功能,而是先建立结构:

  • 你有哪些运行环境(Popup/Content/Service Worker)
  • 它们分别能做什么、不能做什么
  • 你的代码是如何注入网页的(声明式/程序式)
  • 你的数据与指令如何在它们之间流动(消息)
  • 你的权限如何做到"够用但克制"

有了这套骨架,再回头做任何具体功能,都会更顺畅也更好维护。

相关推荐
_Eleven1 小时前
前端布局指南
前端·css
一枚前端小姐姐1 小时前
Vue3 + Vite 从零搭建项目,超详细入门指南
前端·vue.js
赵_叶紫2 小时前
Docker 从入门到部署实战
前端
PD我是你的真爱粉2 小时前
Vue 3 生命周期完全指南:从流程图到最佳实践
前端·vue.js·流程图
耀耀切克闹灬2 小时前
前端签章数据的模板处理
前端
掘金安东尼2 小时前
⏰前端周刊第 454 期(2026年2月16日-2月22日)
前端·javascript·面试
掘金安东尼2 小时前
⏰前端周刊第 453 期(2026年2月9日-2月15日)
前端·javascript·面试
Amumu121382 小时前
CSS进阶导读
前端·css
anyup2 小时前
uniapp开发的鸿蒙应用上架后,竟然月入4000+
前端·vue.js·harmonyos