|
|
|
|
@ -0,0 +1,467 @@
|
|
|
|
|
# 谷歌浏览器插件开发详细教程
|
|
|
|
|
|
|
|
|
|
## 目录
|
|
|
|
|
1. [插件概述](#插件概述)
|
|
|
|
|
2. [开发环境配置](#开发环境配置)
|
|
|
|
|
3. [插件基本结构](#插件基本结构)
|
|
|
|
|
4. [核心组件详解](#核心组件详解)
|
|
|
|
|
5. [常用API](#常用api)
|
|
|
|
|
6. [实战案例](#实战案例)
|
|
|
|
|
7. [调试与发布](#调试与发布)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 1. 插件概述 <a name="插件概述"></a>
|
|
|
|
|
|
|
|
|
|
### 1.1 什么是Chrome扩展程序
|
|
|
|
|
Chrome扩展程序是基于Web技术(HTML、CSS、JavaScript)构建的小程序,用于增强Chrome浏览器的功能。
|
|
|
|
|
|
|
|
|
|
### 1.2 扩展程序类型
|
|
|
|
|
- **浏览器操作扩展**:在工具栏添加图标
|
|
|
|
|
- **页面操作扩展**:针对特定页面生效
|
|
|
|
|
- **内容脚本扩展**:修改网页内容
|
|
|
|
|
- **开发工具扩展**:扩展开发者工具
|
|
|
|
|
- **主题扩展**:改变浏览器外观
|
|
|
|
|
|
|
|
|
|
### 1.3 扩展程序限制
|
|
|
|
|
- 无法访问浏览器核心功能
|
|
|
|
|
- 遵循同源策略
|
|
|
|
|
- 需要明确的权限声明
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 2. 开发环境配置 <a name="开发环境配置"></a>
|
|
|
|
|
|
|
|
|
|
### 2.1 必要工具
|
|
|
|
|
- **Chrome浏览器**:72+版本
|
|
|
|
|
- **代码编辑器**:VS Code、Sublime等
|
|
|
|
|
- **基础前端知识**:HTML、CSS、JavaScript
|
|
|
|
|
|
|
|
|
|
### 2.2 开发模式开启
|
|
|
|
|
1. 打开Chrome浏览器
|
|
|
|
|
2. 访问 `chrome://extensions/`
|
|
|
|
|
3. 开启右上角"开发者模式"
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 3. 插件基本结构 <a name="插件基本结构"></a>
|
|
|
|
|
|
|
|
|
|
### 3.1 最小化结构
|
|
|
|
|
```
|
|
|
|
|
my-extension/
|
|
|
|
|
├── manifest.json # 配置文件
|
|
|
|
|
├── icon.png # 扩展图标
|
|
|
|
|
├── popup.html # 弹出页面
|
|
|
|
|
└── popup.js # 弹出页面脚本
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3.2 manifest.json 详解
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"manifest_version": 3,
|
|
|
|
|
"name": "我的扩展程序",
|
|
|
|
|
"version": "1.0.0",
|
|
|
|
|
"description": "扩展程序描述",
|
|
|
|
|
|
|
|
|
|
"icons": {
|
|
|
|
|
"16": "icons/icon16.png",
|
|
|
|
|
"48": "icons/icon48.png",
|
|
|
|
|
"128": "icons/icon128.png"
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"action": {
|
|
|
|
|
"default_popup": "popup.html",
|
|
|
|
|
"default_title": "点击查看详情",
|
|
|
|
|
"default_icon": {
|
|
|
|
|
"16": "icons/icon16.png",
|
|
|
|
|
"32": "icons/icon32.png"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"permissions": [
|
|
|
|
|
"activeTab",
|
|
|
|
|
"storage",
|
|
|
|
|
"scripting"
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
"host_permissions": [
|
|
|
|
|
"https://*.example.com/*"
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
"background": {
|
|
|
|
|
"service_worker": "background.js"
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"content_scripts": [
|
|
|
|
|
{
|
|
|
|
|
"matches": ["https://*.example.com/*"],
|
|
|
|
|
"js": ["content.js"],
|
|
|
|
|
"css": ["content.css"]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
"web_accessible_resources": [{
|
|
|
|
|
"resources": ["injected.js"],
|
|
|
|
|
"matches": ["<all_urls>"]
|
|
|
|
|
}]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 4. 核心组件详解 <a name="核心组件详解"></a>
|
|
|
|
|
|
|
|
|
|
### 4.1 后台脚本 (Background Script)
|
|
|
|
|
**Manifest V3 使用 Service Worker**
|
|
|
|
|
```javascript
|
|
|
|
|
// background.js
|
|
|
|
|
chrome.runtime.onInstalled.addListener(() => {
|
|
|
|
|
console.log('扩展已安装');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听消息
|
|
|
|
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
|
|
if (message.action === 'getData') {
|
|
|
|
|
sendResponse({ data: '处理后的数据' });
|
|
|
|
|
}
|
|
|
|
|
return true; // 保持消息通道开放
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听浏览器标签页更新
|
|
|
|
|
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
|
|
|
|
if (changeInfo.status === 'complete') {
|
|
|
|
|
// 页面加载完成
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4.2 内容脚本 (Content Script)
|
|
|
|
|
```javascript
|
|
|
|
|
// content.js
|
|
|
|
|
// 直接注入到网页上下文中
|
|
|
|
|
|
|
|
|
|
// 修改页面内容
|
|
|
|
|
document.body.style.backgroundColor = '#f0f0f0';
|
|
|
|
|
|
|
|
|
|
// 监听来自popup或background的消息
|
|
|
|
|
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
|
|
if (request.action === 'extractText') {
|
|
|
|
|
const text = document.body.innerText;
|
|
|
|
|
sendResponse({ text: text.substring(0, 100) });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 向页面注入自定义脚本
|
|
|
|
|
const script = document.createElement('script');
|
|
|
|
|
script.src = chrome.runtime.getURL('injected.js');
|
|
|
|
|
document.head.appendChild(script);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4.3 弹出页面 (Popup)
|
|
|
|
|
```html
|
|
|
|
|
<!-- popup.html -->
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<style>
|
|
|
|
|
body {
|
|
|
|
|
width: 300px;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
font-family: Arial, sans-serif;
|
|
|
|
|
}
|
|
|
|
|
button {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
background: #4285f4;
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h3>我的扩展</h3>
|
|
|
|
|
<button id="actionBtn">执行操作</button>
|
|
|
|
|
<div id="result"></div>
|
|
|
|
|
|
|
|
|
|
<script src="popup.js"></script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// popup.js
|
|
|
|
|
document.getElementById('actionBtn').addEventListener('click', async () => {
|
|
|
|
|
// 获取当前标签页
|
|
|
|
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
|
|
|
|
|
|
|
|
// 发送消息给内容脚本
|
|
|
|
|
const response = await chrome.tabs.sendMessage(tab.id, {
|
|
|
|
|
action: 'getPageInfo'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('result').textContent =
|
|
|
|
|
`页面标题: ${response.title}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 使用存储API
|
|
|
|
|
chrome.storage.local.set({ key: 'value' }, () => {
|
|
|
|
|
console.log('数据已保存');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
chrome.storage.local.get(['key'], (result) => {
|
|
|
|
|
console.log('读取的数据:', result.key);
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 5. 常用API <a name="常用api"></a>
|
|
|
|
|
|
|
|
|
|
### 5.1 消息传递
|
|
|
|
|
```javascript
|
|
|
|
|
// 发送消息
|
|
|
|
|
chrome.runtime.sendMessage(
|
|
|
|
|
{ action: 'doSomething', data: 'some data' },
|
|
|
|
|
response => console.log('响应:', response)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 标签页内通信
|
|
|
|
|
chrome.tabs.sendMessage(tabId, message, callback);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.2 存储API
|
|
|
|
|
```javascript
|
|
|
|
|
// 本地存储
|
|
|
|
|
chrome.storage.local.set({ key: 'value' });
|
|
|
|
|
chrome.storage.local.get(['key'], result => {});
|
|
|
|
|
|
|
|
|
|
// 同步存储(跨设备同步)
|
|
|
|
|
chrome.storage.sync.set({ settings: { theme: 'dark' } });
|
|
|
|
|
|
|
|
|
|
// 监听存储变化
|
|
|
|
|
chrome.storage.onChanged.addListener((changes, area) => {
|
|
|
|
|
for (let key in changes) {
|
|
|
|
|
console.log(`${key} 从 ${changes[key].oldValue} 变为 ${changes[key].newValue}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.3 标签页API
|
|
|
|
|
```javascript
|
|
|
|
|
// 查询标签页
|
|
|
|
|
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
|
|
|
|
|
const currentTab = tabs[0];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 创建新标签页
|
|
|
|
|
chrome.tabs.create({ url: 'https://example.com' });
|
|
|
|
|
|
|
|
|
|
// 更新标签页
|
|
|
|
|
chrome.tabs.update(tabId, { active: true });
|
|
|
|
|
|
|
|
|
|
// 执行脚本
|
|
|
|
|
chrome.scripting.executeScript({
|
|
|
|
|
target: { tabId: tabId },
|
|
|
|
|
func: () => {
|
|
|
|
|
// 在页面中执行的代码
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.4 通知API
|
|
|
|
|
```javascript
|
|
|
|
|
chrome.notifications.create('id', {
|
|
|
|
|
type: 'basic',
|
|
|
|
|
iconUrl: 'icon.png',
|
|
|
|
|
title: '通知标题',
|
|
|
|
|
message: '通知内容'
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 6. 实战案例 <a name="实战案例"></a>
|
|
|
|
|
|
|
|
|
|
### 案例1:网页内容提取器
|
|
|
|
|
**manifest.json**
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"manifest_version": 3,
|
|
|
|
|
"name": "网页内容提取器",
|
|
|
|
|
"version": "1.0",
|
|
|
|
|
"permissions": ["activeTab", "scripting"],
|
|
|
|
|
"action": {
|
|
|
|
|
"default_popup": "popup.html"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**popup.html**
|
|
|
|
|
```html
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<body>
|
|
|
|
|
<h3>内容提取</h3>
|
|
|
|
|
<button id="extract">提取标题和链接</button>
|
|
|
|
|
<div id="output"></div>
|
|
|
|
|
<script src="popup.js"></script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**popup.js**
|
|
|
|
|
```javascript
|
|
|
|
|
document.getElementById('extract').addEventListener('click', async () => {
|
|
|
|
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
|
|
|
|
|
|
|
|
const results = await chrome.scripting.executeScript({
|
|
|
|
|
target: { tabId: tab.id },
|
|
|
|
|
func: () => {
|
|
|
|
|
// 提取页面所有链接
|
|
|
|
|
const links = Array.from(document.querySelectorAll('a'))
|
|
|
|
|
.map(a => ({
|
|
|
|
|
text: a.textContent.trim(),
|
|
|
|
|
href: a.href
|
|
|
|
|
}))
|
|
|
|
|
.filter(link => link.text && link.href);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
title: document.title,
|
|
|
|
|
links: links.slice(0, 10) // 前10个链接
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const output = document.getElementById('output');
|
|
|
|
|
output.innerHTML = `
|
|
|
|
|
<h4>${results[0].result.title}</h4>
|
|
|
|
|
<ul>
|
|
|
|
|
${results[0].result.links.map(link =>
|
|
|
|
|
`<li><a href="${link.href}" target="_blank">${link.text}</a></li>`
|
|
|
|
|
).join('')}
|
|
|
|
|
</ul>
|
|
|
|
|
`;
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 案例2:自定义右键菜单
|
|
|
|
|
**background.js**
|
|
|
|
|
```javascript
|
|
|
|
|
// 创建右键菜单
|
|
|
|
|
chrome.runtime.onInstalled.addListener(() => {
|
|
|
|
|
chrome.contextMenus.create({
|
|
|
|
|
id: "searchGoogle",
|
|
|
|
|
title: "使用Google搜索 '%s'",
|
|
|
|
|
contexts: ["selection"]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
chrome.contextMenus.create({
|
|
|
|
|
id: "saveNote",
|
|
|
|
|
title: "保存选中文本",
|
|
|
|
|
contexts: ["selection"]
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 处理菜单点击
|
|
|
|
|
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
|
|
|
|
switch (info.menuItemId) {
|
|
|
|
|
case "searchGoogle":
|
|
|
|
|
const query = encodeURIComponent(info.selectionText);
|
|
|
|
|
chrome.tabs.create({
|
|
|
|
|
url: `https://www.google.com/search?q=${query}`
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "saveNote":
|
|
|
|
|
chrome.storage.local.get(['notes'], (result) => {
|
|
|
|
|
const notes = result.notes || [];
|
|
|
|
|
notes.push({
|
|
|
|
|
text: info.selectionText,
|
|
|
|
|
url: tab.url,
|
|
|
|
|
timestamp: new Date().toISOString()
|
|
|
|
|
});
|
|
|
|
|
chrome.storage.local.set({ notes });
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 7. 调试与发布 <a name="调试与发布"></a>
|
|
|
|
|
|
|
|
|
|
### 7.1 调试技巧
|
|
|
|
|
1. **弹出页面调试**:右键点击扩展图标 → "检查弹出内容"
|
|
|
|
|
2. **后台脚本调试**:进入扩展管理页面 → 点击"service worker"链接
|
|
|
|
|
3. **内容脚本调试**:在网页开发者工具 → Sources → Content scripts
|
|
|
|
|
4. **网络请求查看**:开发者工具 → Network → 筛选扩展请求
|
|
|
|
|
|
|
|
|
|
### 7.2 控制台日志
|
|
|
|
|
```javascript
|
|
|
|
|
// 不同组件的日志查看位置
|
|
|
|
|
console.log('Background:', message); // 后台脚本控制台
|
|
|
|
|
console.log('Content:', data); // 网页控制台
|
|
|
|
|
console.log('Popup:', info); // 弹出页面控制台
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 7.3 发布到Chrome网上应用店
|
|
|
|
|
1. **准备工作**
|
|
|
|
|
- 准备各种尺寸的图标(16x16, 48x48, 128x128, 512x512)
|
|
|
|
|
- 创建应用商店列表截图(1280x800或640x400)
|
|
|
|
|
- 撰写详细描述和宣传图
|
|
|
|
|
|
|
|
|
|
2. **打包扩展**
|
|
|
|
|
- 打开 `chrome://extensions/`
|
|
|
|
|
- 点击"打包扩展程序"
|
|
|
|
|
- 生成.crx文件和.pem私钥文件
|
|
|
|
|
|
|
|
|
|
3. **提交审核**
|
|
|
|
|
- 访问[Chrome开发者仪表板](https://chrome.google.com/webstore/devconsole/)
|
|
|
|
|
- 支付一次性注册费$5
|
|
|
|
|
- 上传ZIP包并填写信息
|
|
|
|
|
- 等待审核(通常需要几天)
|
|
|
|
|
|
|
|
|
|
### 7.4 常见问题解决
|
|
|
|
|
1. **权限被拒绝**
|
|
|
|
|
- 检查manifest.json中的permissions
|
|
|
|
|
- 确保host_permissions正确配置
|
|
|
|
|
|
|
|
|
|
2. **消息传递失败**
|
|
|
|
|
- 检查组件是否已正确加载
|
|
|
|
|
- 使用try-catch捕获错误
|
|
|
|
|
|
|
|
|
|
3. **存储数据丢失**
|
|
|
|
|
- 使用chrome.storage替代localStorage
|
|
|
|
|
- 重要数据定期备份
|
|
|
|
|
|
|
|
|
|
4. **性能优化**
|
|
|
|
|
- 内容脚本使用延迟加载
|
|
|
|
|
- 避免频繁的storage操作
|
|
|
|
|
- 及时清理事件监听器
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 附录:实用资源
|
|
|
|
|
|
|
|
|
|
### 官方文档
|
|
|
|
|
- [Chrome扩展开发文档](https://developer.chrome.com/docs/extensions/)
|
|
|
|
|
- [Manifest V3迁移指南](https://developer.chrome.com/docs/extensions/mv3/intro/)
|
|
|
|
|
- [API参考手册](https://developer.chrome.com/docs/extensions/reference/)
|
|
|
|
|
|
|
|
|
|
### 开发工具
|
|
|
|
|
- [Chrome扩展样板](https://github.com/GoogleChrome/chrome-extensions-samples)
|
|
|
|
|
- [扩展开发工具](https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin)
|
|
|
|
|
- [CRX Viewer](https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin)
|
|
|
|
|
|
|
|
|
|
### 学习建议
|
|
|
|
|
1. 从简单示例开始,逐步增加复杂度
|
|
|
|
|
2. 充分理解Manifest V3的变化
|
|
|
|
|
3. 注意安全性,避免恶意代码注入
|
|
|
|
|
4. 考虑用户隐私,最小化权限请求
|
|
|
|
|
5. 测试不同网站和场景的兼容性
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
**提示**:开发过程中,经常访问 `chrome://extensions/` 页面进行加载、重载和调试。使用`console.log()`进行调试是最高效的方式之一。祝您开发顺利!
|