Chrome 插件开发实战:5 分钟上手 + 原理深度解析
本文带你从零做出第一个插件,再深入理解底层运行机制。
一、5 分钟做出你的第一个插件
Chrome 插件本质上就是一个文件夹,里面放几个文件。
最小结构(只需 2 个文件)
dart
hello-extension/
├── manifest.json ← 插件的"身份证"
└── popup.html ← 点击图标弹出的页面
动手做
1. 创建 manifest.json
json
{
"manifest_version": 3,
"name": "Hello 插件",
"version": "1.0.0",
"action": {
"default_popup": "popup.html"
}
}
2. 创建 popup.html
html
<!DOCTYPE html>
<html>
<head>
<style>
body { width: 200px; padding: 20px; text-align: center; }
</style>
</head>
<body>
<h2>🎉 成功了!</h2>
</body>
</html>
3. 加载到 Chrome
- 地址栏输入
chrome://extensions/ - 打开右上角「开发者模式」
- 点击「加载已解压的扩展程序」
- 选择文件夹
点击工具栏图标,看到弹窗就成功了!
二、理解插件的运行机制
2.1 插件的三个运行环境
Chrome 插件有三个独立的运行环境,它们各司其职:
scss
┌──────────────────────────────────────────────────────────────┐
│ Chrome 浏览器 │
│ │
│ ┌────────────────┐ │
│ │ Popup │ 用户界面 │
│ │ (弹窗页面) │ • 点击图标时创建,关闭即销毁 │
│ │ │ • 有自己独立的 DOM 和 JS 上下文 │
│ └───────┬────────┘ │
│ │ │
│ │ chrome.scripting.executeScript() │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 网页 (Web Page) │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Content Script │ │ │
│ │ │ (注入的脚本) │ │ │
│ │ │ │ │ │
│ │ │ ✅ 可以访问网页 DOM │ │ │
│ │ │ ✅ 可以读取 localStorage / Cookie │ │ │
│ │ │ ✅ 发起请求时自动携带该网站的登录态 │ │ │
│ │ │ ❌ 不能访问网页的 JS 变量 │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────┐ │
│ │ Background │ 后台服务 │
│ │ (Service Worker)│ • 监听浏览器事件(标签切换、安装等) │
│ │ │ • 处理跨页面的逻辑 │
│ │ │ • 按需唤醒,空闲时休眠 │
│ └────────────────┘ │
└──────────────────────────────────────────────────────────────┘
2.2 为什么要有 Content Script?
这是很多新手困惑的点。看这个场景:
需求:做一个插件,点击按钮后获取当前网页的 Cookie
错误做法:直接在 popup.js 里读
javascript
// ❌ 这样拿到的是插件自己的 Cookie,不是网页的
document.cookie // 空的
正确做法:注入代码到网页执行
javascript
// ✅ 把代码注入到网页上下文执行
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => document.cookie // 这行代码在网页里执行
});
原理图解:
javascript
Popup 的世界 网页的世界
┌─────────────┐ ┌─────────────┐
│ popup.js │ │ example.com │
│ │ │ │
│ document │ │ document │
│ .cookie │ │ .cookie │
│ ↓ │ │ ↓ │
│ 空的 │ 注入代码 → │ "token=xxx" │
└─────────────┘ └─────────────┘
独立沙箱 网页上下文
2.3 三种环境的能力对比
| 能力 | Popup | Content Script | Background |
|---|---|---|---|
| 显示界面 | ✅ | ❌ | ❌ |
| 访问网页 DOM | ❌ | ✅ | ❌ |
| 读取网页 Cookie | ❌ | ✅ | ❌ |
| 发请求带网页登录态 | ❌ | ✅ | ❌ |
| 使用 Chrome APIs | ✅ | 部分 | ✅ |
| 持久运行 | ❌ | ❌ | ✅ |
三、权限系统详解
3.1 权限声明
在 manifest.json 中声明需要的权限:
json
{
"permissions": ["activeTab", "scripting", "storage"],
"host_permissions": ["https://example.com/*"]
}
3.2 常用权限速查
| 权限 | 用途 | 什么时候需要 |
|---|---|---|
activeTab |
访问当前标签页 | 获取页面 URL、标题 |
scripting |
注入脚本到网页 | 操作网页内容 |
storage |
存储数据 | 保存用户设置 |
cookies |
读写 Cookie | 管理登录态 |
tabs |
管理所有标签页 | 批量操作标签 |
notifications |
发送通知 | 提醒用户 |
3.3 host_permissions 的作用
json
"host_permissions": ["https://example.com/*"]
这行配置决定了:
- 你的插件能往哪些网站注入代码
- 你的插件能访问哪些网站的 Cookie
最小权限原则 :只申请需要的域名,不要用 <all_urls>
四、数据通信机制
4.1 Popup → Content Script(最常用)
javascript
// popup.js
const [result] = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: (param) => {
// 这段代码在网页里执行
console.log('收到参数:', param);
return document.title;
},
args: ['hello'] // 传参
});
console.log('网页标题:', result.result);
4.2 各组件间通信
javascript
// 发送消息
chrome.runtime.sendMessage({ action: 'getData' });
// 接收消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'getData') {
sendResponse({ data: 'hello' });
}
});
4.3 数据存储
javascript
// 存储(异步)
await chrome.storage.local.set({ token: 'xxx' });
// 读取
const { token } = await chrome.storage.local.get('token');
// 监听变化
chrome.storage.onChanged.addListener((changes) => {
console.log('数据变了:', changes);
});
五、实战案例:API 调用器
5.1 需求
做一个插件,在 erp.example.com 网站上一键调用 API,自动携带登录态。
5.2 难点分析
scss
直接从 popup.js 发请求:
popup.js → fetch(erp.example.com) → ❌ 跨域,没有 Cookie
解决方案:
popup.js → 注入代码到网页 → 在网页里 fetch → ✅ 同源,有 Cookie
5.3 核心代码
javascript
// popup.js
document.getElementById('btn').onclick = async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const [result] = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: async (apiParams) => {
// ↓↓↓ 这段代码在网页上下文执行 ↓↓↓
// 读取 Cookie 中的 token
const getToken = () => {
const match = document.cookie.match(/token=([^;]+)/);
return match ? match[1] : '';
};
// 发起请求(自动携带 Cookie)
const response = await fetch('/api/data', {
method: 'POST',
headers: {
'Authorization': `Bearer ${getToken()}`,
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(apiParams)
});
return await response.json();
},
args: [{ key: 'value' }]
});
console.log('API 返回:', result.result);
};
5.4 manifest.json
json
{
"manifest_version": 3,
"name": "API 调用器",
"version": "1.0.0",
"action": { "default_popup": "popup.html" },
"permissions": ["activeTab", "scripting"],
"host_permissions": ["https://erp.example.com/*"]
}
六、调试技巧
| 调试目标 | 方法 |
|---|---|
| Popup 页面 | 右键插件图标 → 检查弹出内容 |
| 注入的代码 | 网页 F12 → Console |
| Background | 扩展管理页 → 点击「Service Worker」 |
| 网络请求 | 网页 F12 → Network |
热更新 :修改代码后,去 chrome://extensions/ 点刷新按钮
七、常见坑点
7.1 图标不显示
css
❌ icon.svg → Chrome 不支持 SVG
✅ icon.png → 必须是 PNG,建议 128x128
7.2 代码不生效
javascript
// ❌ 错误:executeScript 的 func 不能引用外部变量
const url = 'https://api.com';
chrome.scripting.executeScript({
func: () => fetch(url) // url 是 undefined!
});
// ✅ 正确:通过 args 传参
chrome.scripting.executeScript({
func: (url) => fetch(url),
args: ['https://api.com']
});
7.3 跨域问题
sql
症状:fetch 报 CORS 错误
原因:从 popup.js 直接请求其他域名
解决:用 executeScript 注入到目标网页执行
八、发布到 Chrome 商店
- 注册开发者账号(一次性 $5)
- 打包成 zip(不含 node_modules)
- 上传到 Chrome Web Store
- 填写描述、截图、隐私政策
- 等待审核(1-3 天)
九、总结
css
Chrome 插件 = manifest.json + 界面 + 逻辑
三个运行环境:
├── Popup → 用户界面,点击图标显示
├── Content → 注入网页,操作页面内容
└── Background → 后台服务,监听事件
核心技能:
├── chrome.scripting.executeScript → 注入代码到网页
├── chrome.storage → 存储数据
└── chrome.runtime.sendMessage → 组件间通信
掌握这些,你就能开发 90% 的 Chrome 插件了。