Chrome 插件开发实战:从入门到进阶
一、引言
1.1 Chrome 插件简介
Chrome 插件,即扩展程序,是为增强 Google Chrome 浏览器功能而设计的小型软件。它能为用户提供丰富功能,如广告拦截、密码管理、页面翻译等,极大提升浏览体验与工作效率。例如,AdBlock 可屏蔽网页广告,LastPass 能安全管理密码,Grammarly 可检查拼写语法错误。
Chrome 插件运行在浏览器沙盒环境,基于 HTML、CSS 和 JavaScript 等 Web 技术开发,开发者可借助浏览器 API 实现与浏览器及网页内容的交互。
1.2 开发 Chrome 插件的意义与价值
对于开发者,开发 Chrome 插件可拓展技术能力,将创意转化为实用工具,还能通过 Chrome Web Store 发布作品获取收益与用户反馈。对用户而言,插件能定制浏览体验,满足个性化需求,提高上网效率与乐趣。插件还能促进浏览器生态发展,推动更多创新应用出现。
1.3 本文目标与读者对象
本文旨在带领读者全面了解 Chrome 插件开发流程,从基础概念到实战开发,再到发布维护,助力读者掌握开发技能,开发出实用插件。适合有一定 Web 开发基础(熟悉 HTML、CSS、JavaScript),想学习 Chrome 插件开发的初学者,以及有相关经验但希望深入了解的开发者。
二、Chrome 插件开发基础
2.1 开发环境搭建
2.1.1 安装 Chrome 浏览器
确保安装最新版本的 Chrome 浏览器,可从 Chrome 官网(https://www.google.com/chrome/)下载。新版本通常支持更多功能与 API,利于开发。
2.1.2 启用开发者模式
打开 Chrome 浏览器,进入菜单 “更多工具”>“扩展程序”,打开右上角的 “开发者模式” 切换按钮。开启后可加载未打包的扩展程序,方便开发调试。
2.1.3 选择合适的文本编辑器
推荐使用 Visual Studio Code,它功能强大,有丰富插件支持,如 ESLint 可检查 JavaScript 代码规范,Live Server 可实时预览网页。也可选择 Sublime Text、Atom 等。
2.2 Chrome 插件的基本结构
2.2.1 manifest.json 文件详解
manifest.json 是插件核心配置文件,定义插件基本信息、权限、功能等。关键字段如下:
- manifest_version:指定清单文件版本,当前常用 2 或 3,建议用 3 以获取新特性与安全改进。
- name:插件名称,显示在 Chrome 插件管理页面和 Chrome 网上应用店。
- version:插件版本号,采用语义化版本控制,如 “1.0”“1.1.1”。
- description:插件简短描述,解释功能用途。
- icons:定义不同尺寸图标,用于浏览器扩展程序页面和工具栏,如:
json
"icons": {"16": "icon16.png","48": "icon48.png","128": "icon128.png"
}
- browser_action 或 page_action:定义浏览器操作按钮或页面操作按钮行为。browser_action 用于常用操作,点击后显示弹出窗口;page_action 用于特定页面操作,按钮在匹配页面才显示。如:
json
"browser_action": {"default_popup": "popup.html","default_icon": "icon.png"
}
- permissions:插件所需 API 权限列表,如 “activeTab” 可访问当前活动标签页,“storage” 可进行数据存储。
- background:指定后台脚本,MV3 中用 service_worker 字段指定,如:
json
"background": {"service_worker": "background.js"
}
- content_scripts:定义内容脚本及其注入页面,如:
json
"content_scripts": [{"matches": ["<all_urls>"],"js": ["content.js"]}
]
表示 content.js 会注入到所有页面。
2.2.2 背景脚本(Background Scripts)
背景脚本在浏览器后台运行,处理长时间任务,如事件监听、消息传递、定时任务。MV3 中用 Service Worker 代替后台页面,提高性能与安全性。例如监听插件安装事件:
javascript
// background.js
chrome.runtime.onInstalled.addListener(() => {console.log('插件已安装');
});
还可监听标签页更新事件:
javascript
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {if (changeInfo.status === 'complete') {console.log('标签页', tabId, '已更新');}
});
2.2.3 内容脚本(Content Scripts)
内容脚本注入到网页,可直接访问修改网页 DOM。例如,修改网页背景颜色:
javascript
// content.js
document.body.style.backgroundColor = 'lightblue';
内容脚本与网页 DOM 交互,但不能直接访问浏览器 API,需通过消息传递与背景脚本通信。
2.2.4 弹出页面(Popup Pages)
弹出页面是插件用户界面,用户点击插件图标时显示。通常包含 HTML、CSS 和 JavaScript 文件。如 popup.html:
html
<!DOCTYPE html>
<html lang="zh - CN">
<head><meta charset="UTF - 8"><meta name="viewport" content="width=device - width, initial - scale = 1.0"><title>插件弹出页面</title><link rel="stylesheet" href="popup.css"><script src="popup.js"></script>
</head>
<body><h1>这是一个简单的弹出页面</h1><button id="click - me">点击我</button>
</body>
</html>
popup.js 处理按钮点击事件:
javascript
document.getElementById('click - me').addEventListener('click', () => {alert('按钮被点击了');
});
2.2.5 选项页面(Options Pages)
选项页面是插件设置页面,用户可自定义插件行为。创建选项页面需在 manifest.json 声明:
json
"options_page": "options.html"
options.html 示例:
html
<!DOCTYPE html>
<html lang="zh - CN">
<head><meta charset="UTF - 8"><meta name="viewport" content="width=device - width, initial - scale = 1.0"><title>插件选项页面</title><link rel="stylesheet" href="options.css"><script src="options.js"></script>
</head>
<body><h1>插件设置</h1><label for="color - setting">选择颜色:</label><input type="color" id="color - setting"><button id="save - settings">保存设置</button>
</body>
</html>
options.js 保存用户设置:
javascript
document.getElementById('save - settings').addEventListener('click', () => {const color = document.getElementById('color - setting').value;chrome.storage.sync.set({ colorSetting: color }, () => {console.log('设置已保存');});
});
2.3 Chrome 插件的生命周期与事件系统
2.3.1 插件生命周期
插件生命周期包括安装、更新、启动、运行、停止、卸载阶段。
- 安装或更新:用户首次安装或插件有新版本时,浏览器加载初始化插件。可在 background 脚本监听 chrome.runtime.onInstalled 事件执行初始化操作,通过 reason 判断是安装还是更新:
javascript
chrome.runtime.onInstalled.addListener((details) => {if (details.reason === 'install') {console.log('插件首次安装');} else if (details.reason === 'update') {console.log('插件更新,旧版本:', details.previousVersion);}
});
- 启动:用户打开浏览器,插件启动。可在此阶段初始化数据、设置默认状态。
- 运行:插件启动后进入运行阶段,响应用户操作,监听处理浏览器事件,提供功能。
- 停止:用户关闭浏览器,插件停止。可监听 chrome.runtime.onSuspend 事件保存数据、清理资源,但 Chrome 未提供浏览器关闭直接事件,可用 chrome.windows.onRemoved 事件在最后一个浏览器窗口关闭时执行操作:
javascript
let windowCount = 0;
chrome.windows.getAll((windows) => {windowCount = windows.length;
});
chrome.windows.onRemoved.addListener((windowId) => {windowCount--;if (windowCount === 0) {// 执行清理操作console.log('所有窗口已关闭,插件停止');}
});
- 卸载:用户卸载插件,生命周期结束。可监听 chrome.runtime.onInstalled 事件的 uninstall 原因执行卸载操作:
javascript
chrome.runtime.onInstalled.addListener((details) => {if (details.reason === 'uninstall') {console.log('插件被卸载');}
});
2.3.2 事件系统
Chrome 插件事件系统让插件响应浏览器和用户事件。
- 浏览器事件:
- 浏览器启动事件:监听 chrome.runtime.onStartup 事件,如:
javascript
chrome.runtime.onStartup.addListener(() => {console.log('浏览器启动');
});
- 打开新窗口事件:监听 chrome.windows.onCreated 事件,如:
javascript
chrome.windows.onCreated.addListener((window) => {console.log('新窗口打开,窗口ID:', window.id);
});
- 关闭窗口事件:监听 chrome.windows.onRemoved 事件,如:
javascript
chrome.windows.onRemoved.addListener((windowId) => {console.log('窗口关闭,窗口ID:', windowId);
});
- 切换标签页事件:监听 chrome.tabs.onActivated 事件,如:
javascript
chrome.tabs.onActivated.addListener((activeInfo) => {console.log('标签页切换,新标签页ID:', activeInfo.tabId);
});
- 网络事件:
- 请求发送事件:监听 chrome.webRequest.onBeforeRequest 事件,可拦截修改请求,如:
javascript
chrome.webRequest.onBeforeRequest.addListener((details) => {if (details.url.includes('example.com')) {return { redirectUrl: 'https://new - example.com' };}},{ urls: ['<all_urls>'] },['blocking']
);
- 响应接收事件:监听 chrome.webRequest.onCompleted 事件,如:
javascript
chrome.webRequest.onCompleted.addListener((details) => {console.log('请求完成,URL:', details.url);},{ urls: ['<all_urls>'] }
);
- 连接错误事件:监听 chrome.webRequest.onErrorOccurred 事件,如:
javascript
chrome.webRequest.onErrorOccurred.addListener((details) => {console.log('连接错误,URL:', details.url, ',错误信息:', details.error);},{ urls: ['<all_urls>'] }
);
- 用户交互事件:
- 点击插件图标事件:监听 chrome.browserAction.onClicked 事件,如:
javascript
chrome.browserAction.onClicked.addListener((tab) => {console.log('插件图标被点击,当前标签页ID:', tab.id);
});
- 选择插件菜单事件:先创建菜单,再监听 chrome.contextMenus.onClicked 事件,如:
javascript
chrome.contextMenus.create({id: "sampleContextMenu",title: "示例菜单",contexts: ["page"]
});
chrome.contextMenus.onClicked.addListener((info, tab) => {if (info.menuItemId === "sampleContextMenu") {console.log('示例菜单被选择');}
});
- 使用快捷键事件:在 manifest.json 定义快捷键,监听 chrome.commands.onCommand 事件,如:
json
"commands": {"toggle - popup": {"suggested_key": {"default": "Ctrl+Shift+P","mac": "Command+Shift+P"},"description": "Toggle the popup"}
}
javascript
chrome.commands.onCommand.addListener((command) => {if (command === 'toggle - popup') {console.log('快捷键被使用,打开或关闭弹出窗口');}
});
三、实战案例:开发一个简单的 Chrome 插件
3.1 插件功能需求分析
我们开发一个 “页面信息展示” 插件,功能如下:
- 点击插件图标,弹出窗口显示当前页面 URL、标题和加载时间。
- 提供选项页面,用户可设置是否在弹出窗口显示页面描述(若有)。
3.2 创建项目结构
在项目目录创建以下文件结构:
plaintext
my - page - info - extension/
│
├── manifest.json
│
├── background.js
│
├── popup.html
│
├── popup.js
│
├── options.html
│
├── options.js
│
├── icons/
│ ├── icon16.png
│ ├── icon48.png
│ ├── icon128.png
│
└── styles/├── popup.css├── options.css
3.3 编写 manifest.json 文件
json
{"manifest_version": 3,"name": "页面信息展示插件","version": "1.0","description": "显示当前页面的URL、标题、加载时间等信息","icons": {"16": "icons/icon16.png","48": "icons/icon48.png","128": "icons/icon128.png"},"action": {"default_popup": "popup.html","default_icon": "icons/icon48.png"},"options_page": "options.html","background": {"service_worker": "background.js"},"permissions": ["activeTab","storage"]
}
3.4 开发背景脚本(background.js)
背景脚本监听页面加载完成事件,记录页面加载时间:
javascript
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {if (changeInfo.status === 'complete') {const startTime = performance.now();chrome.scripting.executeScript({target: { tabId: tabId },function: () => performance.now()}).then((results) => {const endTime = results[0].result;const loadTime = endTime - startTime;chrome.storage.local.set({ [tabId]: loadTime });});}
});
3.5 实现弹出页面(popup.html 和 popup.js)
popup.html 结构:
html
<!DOCTYPE html>
<html lang="zh - CN">
<head><meta charset="UTF - 8"><meta name="viewport" content="width=device - width, initial - scale = 1.0"><title>页面信息</title><link rel="stylesheet" href="styles/popup.css"><script src="popup.js"></script>
</head>
<body><h1>页面信息</h1><div id="url - info"><strong>URL:</strong><span id="page - url"></span></div><div id="title - info"><strong>标题:</strong><span id="page - title"></span></div><div id="load - time - info"><strong>加载时间:</strong><span id="page - load - time"></span>毫秒</div><div id="description - info"><strong>描述:</strong><span id="page - description"></span></div>
</body>
</html>
popup.js 获取并显示页面信息:
javascript
document.addEventListener('DOMContentLoaded', () => {chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {const tab = tabs[0];document.getElementById('page - url').textContent = tab.url;document.getElementById('page - title').textContent = tab.title;chrome.storage.local.get([tab.id], (data) => {document.getElementById('page - load - time').textContent = data[tab.id] || '未知';});chrome.scripting.executeScript({target: { tabId: tab.id },function: () => {const meta = document.querySelector('meta[name="description"]');return meta? meta.content : '';}}).then((results) => {document.getElementById('page - description').textContent = results[0].result;});});
});
3.6 设计选项页面(options.html 和 options.js)
options.html 结构:
html
<!DOCTYPE html>
<html lang="zh - CN">
<head><meta charset="UTF - 8"><meta name="viewport" content="width=device - width, initial - scale = 1.0"><title>插件选项</title><link rel="stylesheet" href="styles/options.css"><script src="options.js"></script>
</head>
<body><h1>插件选项</h1><input type