Chrome浏览器插件开发教程
2025-12-24
插件是个啥
Chrome插件就是给浏览器加功能的小程序。比如广告拦截、翻译、密码管理器这些都是插件。你写个插件可以:
改网页的内容和样式
给浏览器加新功能
调用外部接口
管理书签和标签页
抓取网页数据
基本上想给浏览器加什么功能都行。
准备工作
必须有的
Chrome浏览器(最好是最新版)
代码编辑器(VSCode挺好用)
会写HTML、CSS、JavaScript
可选的
Node.js(用来装一些开发工具)
Git(管理代码版本)
插件的文件结构
一个插件大概长这样:
我的插件/
├── manifest.json # 配置文件,必须有
├── popup.html # 点击插件图标弹出的页面
├── popup.js # 弹窗的JS代码
├── background.js # 后台运行的代码
├── content.js # 注入到网页的代码
├── options.html # 设置页面
├── options.js # 设置页面的代码
└── icons/ # 图标文件夹
├── icon16.png
├── icon48.png
└── icon128.png
第一个插件
咱们先做个简单的"Hello World"插件。
第一步:新建文件夹
随便找个地方新建一个文件夹,比如叫 hello-world。
第二步:写配置文件manifest.json
{
"manifest_version": 3,
"name": "Hello World 插件",
"version": "1.0",
"description": "我的第一个插件",
"permissions": [
"activeTab"
],
"action": {
"default_popup": "popup.html",
"default_title": "Hello World",
"default_icon": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
第三步:弹窗页面popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
width: 300px;
height: 200px;
padding: 20px;
font-family: "微软雅黑", sans-serif;
}
button {
background: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #0056b3;
}
#result {
margin-top: 15px;
color: #28a745;
font-weight: bold;
}
</style>
</head>
<body>
<h2>你好世界!</h2>
<p>这是我做的第一个Chrome插件</p>
<button id="clickMe">点我试试</button>
<p id="result"></p>
<script src="popup.js"></script>
</body>
</html>
第四步:弹窗逻辑popup.js
document.addEventListener('DOMContentLoaded', function() {
const button = document.getElementById('clickMe');
const result = document.getElementById('result');
button.addEventListener('click', function() {
result.textContent = '恭喜!插件运行成功了!';
});
});
第五步:准备图标
在 icons 文件夹里放几个不同尺寸的图标,16、32、48、128像素的PNG文件。
第六步:装载插件
打开Chrome,地址栏输入
chrome://extensions/右上角打开"开发者模式"
点"加载已解压的扩展程序"
选择你的插件文件夹
装好后工具栏就会出现你的插件图标,点击试试看!
核心文件说明
manifest.json配置详解
这个文件就是插件的身份证,告诉浏览器这个插件是干嘛的:
{
"manifest_version": 3, // 版本号,现在都用3
"name": "插件名字",
"version": "1.0.0", // 你的插件版本
"description": "插件介绍",
// 申请的权限
"permissions": [
"storage", // 能存数据
"activeTab", // 能操作当前标签页
"tabs" // 能操作所有标签页
],
// 能访问的网站(V3新加的)
"host_permissions": [
"https://*.baidu.com/*"
],
// 后台脚本
"background": {
"service_worker": "background.js"
},
// 网页注入脚本
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content.js"],
"css": ["content.css"]
}],
// 工具栏图标
"action": {
"default_popup": "popup.html",
"default_title": "鼠标悬停显示的文字",
"default_icon": "icon.png"
},
// 选项页面
"options_page": "options.html",
// 各种尺寸图标
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
}
后台脚本background.js
这个脚本在后台一直运行,处理各种事件:
// background.js
// 插件装好后执行
chrome.runtime.onInstalled.addListener(() => {
console.log('插件装好了');
});
// 监听标签页变化
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete' && tab.url) {
console.log('页面加载完了:', tab.url);
}
});
// 点击插件图标时执行
chrome.action.onClicked.addListener((tab) => {
// 给当前页面发消息
chrome.tabs.sendMessage(tab.id, {action: "doSomething"});
});
内容脚本content.js
这个脚本会注入到网页里,可以操作页面元素:
// content.js
// 给页面加个按钮
function addMyButton() {
const button = document.createElement('button');
button.textContent = '我是插件按钮';
button.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
padding: 10px 15px;
background: #ff6b6b;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
`;
button.onclick = function() {
alert('你好,我是从插件来的!');
};
document.body.appendChild(button);
}
// 收后台脚本的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "doSomething") {
addMyButton();
}
});
// 页面加载完就执行
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addMyButton);
} else {
addMyButton();
}
权限设置
Chrome插件要申请权限才能做事,这是为了保护用户隐私:
常用权限列表
{
"permissions": [
"storage", // 存储数据
"activeTab", // 操作当前标签页
"tabs", // 操作所有标签页
"bookmarks", // 操作书签
"history", // 查看浏览历史
"cookies", // 读写Cookie
"notifications", // 发通知
"contextMenus", // 右键菜单
"background" // 后台运行
],
"host_permissions": [
"https://*.taobao.com/*", // 访问淘宝
"https://*.jd.com/*", // 访问京东
"<all_urls>" // 访问所有网站(慎用)
]
}
权限要的越少越好,用户安装时顾虑也少。
不同脚本间的通信
插件里popup、background、content script要互相传消息:
Popup和Background通信
// popup.js - 发消息
chrome.runtime.sendMessage({type: "getData", params: "something"}, (response) => {
console.log('收到回复:', response);
});
// background.js - 收消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === "getData") {
// 处理请求
const result = processData(request.params);
sendResponse({success: true, data: result});
}
});
Content Script和Background通信
// content.js - 给后台发消息
chrome.runtime.sendMessage({
type: "pageAnalyzed",
url: window.location.href,
title: document.title
});
// background.js - 给content script发消息
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, {
type: "highlightText",
keyword: "关键词"
});
});
数据存储
插件可以用几种方式存数据:
Chrome Storage API(推荐)
// 存数据
chrome.storage.sync.set({
username: "张三",
settings: {theme: "dark", lang: "zh"}
}, () => {
console.log('保存成功');
});
// 取数据
chrome.storage.sync.get(['username', 'settings'], (result) => {
console.log('用户名:', result.username);
console.log('设置:', result.settings);
});
// 监听数据变化
chrome.storage.onChanged.addListener((changes, namespace) => {
for (let key in changes) {
console.log(`${key} 从 ${changes[key].oldValue} 改成了 ${changes[key].newValue}`);
}
});
// 删除数据
chrome.storage.sync.remove('username', () => {
console.log('删除成功');
});
localStorage(只能在content script里用)
// 在content script中
localStorage.setItem('myData', JSON.stringify({name: '数据', value: 123}));
const data = JSON.parse(localStorage.getItem('myData'));
调试方法
各种调试方式
调试popup:右键插件图标 → "检查弹出内容"
调试background:扩展管理页面点"检查视图"
调试content script:网页F12,在Sources里找扩展脚本
调试技巧
// 打日志
console.log('这里是调试信息:', someData);
console.error('出错了:', error);
// 断点调试
debugger; // 程序会在这里停下
// 检查是否有错
if (chrome.runtime.lastError) {
console.error('Chrome API出错:', chrome.runtime.lastError);
}
// 捕获异常
try {
// 可能出错的代码
doSomethingRisky();
} catch (error) {
console.error('捕获到异常:', error);
}
上架发布
发布前准备
完善插件信息:描述要详细,图标要好看
全面测试:各种浏览器版本都试试
准备素材:截图、宣传图、详细说明
发布到Chrome应用商店
注册Chrome开发者账号(要交5美元注册费)
打开 Chrome开发者后台
点"新增项目"
上传插件ZIP包
填写详细信息
提交审核(通常几天到一周)
打包插件
# 把插件文件夹压缩成ZIP
zip -r 我的插件.zip 插件文件夹/
记住别把开发文件(.git、node_modules等)打包进去。
高级功能
右键菜单
// background.js
chrome.runtime.onInstalled.addListener(() => {
// 创建右键菜单项
chrome.contextMenus.create({
id: "translateText",
title: "翻译选中文字",
contexts: ["selection"] // 只有选中文字时才显示
});
});
// 处理右键菜单点击
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "translateText") {
// 翻译选中的文字
const selectedText = info.selectionText;
translateText(selectedText);
}
});
桌面通知
// 发送通知
chrome.notifications.create('notification-id', {
type: 'basic',
iconUrl: 'icon48.png',
title: '插件通知',
message: '这是来自插件的消息!'
}, (notificationId) => {
console.log('通知已发送:', notificationId);
});
// 监听通知点击
chrome.notifications.onClicked.addListener((notificationId) => {
console.log('用户点击了通知:', notificationId);
});
标签页操作
// 新建标签页
chrome.tabs.create({
url: 'https://www.baidu.com',
active: true // 立即切换到新标签页
});
// 关闭当前标签页
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.remove(tabs[0].id);
});
// 刷新页面
chrome.tabs.reload();
// 获取所有标签页
chrome.tabs.query({}, (tabs) => {
tabs.forEach(tab => {
console.log(tab.title, tab.url);
});
});
开发经验
性能优化
// 避免频繁操作DOM
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `第${i}个元素`;
fragment.appendChild(div);
}
document.body.appendChild(fragment); // 一次性添加
// 及时移除事件监听
let tabUpdateListener = (tabId, changeInfo, tab) => {
// 处理标签页更新
};
// 开始监听
chrome.tabs.onUpdated.addListener(tabUpdateListener);
// 不需要时移除监听
chrome.tabs.onUpdated.removeListener(tabUpdateListener);
安全建议
// 验证URL是否安全
function isValidUrl(url) {
try {
const urlObj = new URL(url);
return urlObj.protocol === 'https:' || urlObj.protocol === 'http:';
} catch {
return false;
}
}
// 过滤用户输入
function sanitizeInput(input) {
return input.replace(/[<>'"]/g, ''); // 简单过滤
}
// 验证消息来源
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
// 确保消息来自插件内部
if (!sender.tab || sender.tab.url.startsWith('chrome-extension://')) {
// 处理消息
}
});
用户体验
// 显示加载状态
function showLoading() {
document.getElementById('loading').style.display = 'block';
}
function hideLoading() {
document.getElementById('loading').style.display = 'none';
}
// 友好的错误提示
function showError(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = `出错了:${message}`;
document.body.appendChild(errorDiv);
// 3秒后自动消失
setTimeout(() => {
errorDiv.remove();
}, 3000);
}
// 处理异步操作
async function fetchUserData() {
try {
showLoading();
const response = await fetch('https://api.example.com/user');
if (!response.ok) {
throw new Error(`服务器返回${response.status}错误`);
}
const data = await response.json();
return data;
} catch (error) {
showError('获取用户数据失败,请稍后重试');
console.error('API调用失败:', error);
} finally {
hideLoading();
}
}
实战项目:网页截图工具
我们做个实用的插件,能截图当前网页:
manifest.json
{
"manifest_version": 3,
"name": "网页截图助手",
"version": "1.0",
"description": "快速截取网页截图",
"permissions": [
"activeTab",
"downloads"
],
"action": {
"default_popup": "popup.html",
"default_title": "网页截图"
},
"background": {
"service_worker": "background.js"
}
}
popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
width: 280px;
padding: 20px;
font-family: "微软雅黑", Arial, sans-serif;
}
button {
width: 100%;
padding: 12px;
margin: 8px 0;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.primary-btn {
background: #007bff;
color: white;
}
.primary-btn:hover {
background: #0056b3;
}
.secondary-btn {
background: #6c757d;
color: white;
}
.secondary-btn:hover {
background: #545b62;
}
#status {
margin-top: 15px;
padding: 10px;
border-radius: 4px;
text-align: center;
display: none;
}
.success {
background: #d4edda;
color: #155724;
}
.error {
background: #f8d7da;
color: #721c24;
}
</style>
</head>
<body>
<h3>网页截图工具</h3>
<button id="captureVisible" class="primary-btn">截取可见区域</button>
<button id="captureFullPage" class="secondary-btn">截取整个页面</button>
<div id="status"></div>
<script src="popup.js"></script>
</body>
</html>
popup.js
document.addEventListener('DOMContentLoaded', function() {
const captureVisibleBtn = document.getElementById('captureVisible');
const captureFullPageBtn = document.getElementById('captureFullPage');
const status = document.getElementById('status');
function showStatus(message, type = 'success') {
status.textContent = message;
status.className = type;
status.style.display = 'block';
setTimeout(() => {
status.style.display = 'none';
}, 3000);
}
captureVisibleBtn.addEventListener('click', () => {
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.runtime.sendMessage({
action: 'captureVisible',
tab: tabs[0]
}, (response) => {
if (response.success) {
showStatus('截图成功!');
} else {
showStatus('截图失败:' + response.error, 'error');
}
});
});
});
captureFullPageBtn.addEventListener('click', () => {
showStatus('全页截图功能开发中...', 'error');
});
});
background.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'captureVisible') {
captureVisibleTab(request.tab)
.then(dataUrl => {
downloadImage(dataUrl, request.tab.title);
sendResponse({success: true});
})
.catch(error => {
console.error('截图失败:', error);
sendResponse({success: false, error: error.message});
});
return true; // 异步响应
}
});
async function captureVisibleTab(tab) {
try {
const dataUrl = await chrome.tabs.captureVisibleTab(tab.windowId, {
format: 'png',
quality: 100
});
return dataUrl;
} catch (error) {
throw new Error('无法截取当前页面');
}
}
function downloadImage(dataUrl, pageTitle) {
const now = new Date();
const timestamp = now.toISOString().replace(/[:.]/g, '-');
const filename = `截图_${pageTitle || 'unnamed'}_${timestamp}.png`;
chrome.downloads.download({
url: dataUrl,
filename: filename,
saveAs: false
});
}
这个插件就能截图了!点击按钮会自动下载当前页面的截图。
总结
Chrome插件开发其实不难,主要是:
理解结构:manifest.json + 各种脚本文件
掌握通信:popup、background、content script之间怎么传消息
熟练API:Chrome提供的各种接口
多练习:从简单项目开始,慢慢做复杂功能
继续学习
看Chrome官方文档了解更多API
学习现代前端框架(React、Vue)开发插件
了解Webpack等构建工具
学习自动化测试
有用链接
记住一点:最好的学习方法就是动手做。想到什么功能就试着实现,遇到问题就查文档,这样进步最快。