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