Chrome插件初探

284 阅读7分钟

Chrome插件是使用HTMLCSSJavaScript Web 技术构建的,开发者可以利用这些技术在浏览器中添加新功能、修改现有功能或者与网页进行交互。本文主要介绍如何使用Manifest V3快速搭建一个Chrome插件。

Manifest V3是Chrome插件的最新版本,相较于V2,主要改进以下几点:

  • 更新manifest manifest.json 必须指定 V3。部分配置项进行了修改。
  • 迁移到 Service Worker Service Worker 会替换扩展程序的后台或事件页面,以确保后台代码远离主线程,从而降低性能。此更改还将 DOM、窗口和某些扩展 API 调用移入屏幕外的文档中。
  • 更新 API 调用 某些 API 调用需要替换为更现代的等效项。
  • 替换阻止网络请求监听器 在 Manifest V2 中屏蔽或修改网络请求可能会显著降低性能,并导致需要过多访问敏感用户数据。借助 Declarative Net Request API,扩展程序可以使用较少的权限来屏蔽或修改 Web 内容,而不会降低性能。
  • 提高扩展程序安全性 Manifest V3 通过多种方式提高扩展程序的安全性。除了增强的内容安全政策之外,不再支持远程托管代码和任意字符串的执行。

基础配置manifest.json

开发chrome插件,最重要的文件 manifest.json,基础信息如下:

{
    "name": "chrome-extension", // 插件名称
    "version": "1.0", // 插件版本
    "description": "chrome extension", // 插件描述
    "manifest_version": 3, // manifest版本,涉及到api的变化,最新为3
    "icons": { // 设置不同尺寸图标
        "16": "images/icon-16.png",
        "32": "images/icon-32.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
    }
}

当前目录结构

.
├── images
│   └── icon-16.png
│   └── icon-32.png
│   └── icon-48.png
│   └── icon-128.png
├── manifest.json

弹窗页面action

可以通过配置action实现点击图标触发插件

{
    "name": "chrome-extension",
    "version": "1.0",
    "description": "chrome extension",
    "manifest_version": 3,
    "icons": {
        "16": "images/icon-16.png",
        "32": "images/icon-32.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
    }"action": {
        "default_icon": { // 工具栏图标优先使用
            "16": "images/default_icon-16.png",
            "32": "images/default_icon-32.png",
            "48": "images/default_icon-48.png",
            "128": "images/default_icon-128.png"
        },
        "default_title": "Click", // 弹窗标题
        "default_popup": "popup/popup.html" // 弹窗页面
    },
}

创建popup.html, popup.csspopup.js 文件

.
├── images
│   └── icon-16.png
│   └── icon-32.png
│   └── icon-48.png
│   └── icon-128.png
│   └── default_icon-16.png
│   └── default_icon-32.png
│   └── default_icon-48.png
│   └── default_icon-128.png
├── manifest.json
├── popup
│   └── popup.html
│   └── popup.css
│   └── popup.js

popup.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>

<body>
    <div class="hint">
        <div>Thank you for using ChatAssistant!</div>
        <button id="refresh-btn">refresh</button>
    </div>
    <script src="popup/popup.js"></script>
    <script src="popup/popup.css"></script>
</body>

</html>

popup.css

.hint {
    width: 200px;
}

popup.js

document.getElementById('refresh-btn').addEventListener('click', () => {
    // 处理逻辑
    chrome.tabs.reload(); // 网页刷新
    window.close(); // 关闭弹窗
});

后台脚本background

后台处理脚本,在浏览器内跨Tab运行,可以完成跨域请求、网页截屏、弹出Chrome通知等重要浏览器事件的监听,但无法改变浏览器内容。

{
    "name": "chrome-extension",
    "version": "1.0",
    "description": "chrome extension",
    "manifest_version": 3,
    "icons": {
        "16": "images/icon-16.png",
        "32": "images/icon-32.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
    }"background": {
        "service_worker": "background.js", // 脚本路径
        "type": "module"
    },
}

新建background.js

.
├── images
│   └── icon-16.png
│   └── icon-32.png
│   └── icon-48.png
│   └── icon-128.png
│   └── default_icon-16.png
│   └── default_icon-32.png
│   └── default_icon-48.png
│   └── default_icon-128.png
├── manifest.json
├── background.js
// 当插件首次安装或更新时,会触发此事件,并执行回调函数。
chrome.runtime.onInstalled.addListener(() => {
  console.log('扩展插件已安装!');
});

// 在用户点击操作图标时触发。如果操作具有弹出式窗口(定义了default_popup),则不会触发此事件。
chrome.action.onClicked.addListener((tab: any) => {
    chrome.tabs.sendMessage(tab.id, {
        message: TIMING_WHEN_CLICK_EXTENSION_ICON,
    });
});

内容脚本content_scripts

content_scripts在单Tab页隔离运行的内容脚本,不会与网页或其他扩展程序的内容脚本发生冲突,能够读取并更改网页内容,可以进行DOM操作。

{
    "name": "chrome-extension",
    "version": "1.0",
    "description": "chrome extension",
    "manifest_version": 3,
    "icons": {
        "16": "images/icon-16.png",
        "32": "images/icon-32.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
    }"background": {
        "service_worker": "background.js",
        "type": "module"
    },
    "content_scripts": [ 
        {
            "matches": [  // 指定要将此内容脚本注入哪些网页
                "<all_urls>"
            ],
            "css": [
                "content/content.css"
            ],
            "js": [
                "content/content.js"
            ],
            "run_at": "document_end" // 指定何时应将脚本注入到网页中
        }
    ],
}

新建content.js,处理复杂页面逻辑

.
├── images
│   └── icon-16.png
│   └── icon-32.png
│   └── icon-48.png
│   └── icon-128.png
│   └── default_icon-16.png
│   └── default_icon-32.png
│   └── default_icon-48.png
│   └── default_icon-128.png
├── manifest.json
├── background.js
├── content
│   └── content.js
│   └── content.css

content.js

chrome.runtime.onMessage.addListener((request: any, sender: chrome.runtime.MessageSender, sendResponse: any) => {
    // 接收到信息开始处理逻辑
});

权限permissions

需要在permissions中添加所需权限,这能帮助用户清楚为什需要此权限,且仅在必要的时候才运行扩展使用该权限。附可选权限 permissions

{
    "name": "chrome-extension",
    "version": "1.0",
    "description": "chrome extension",
    "manifest_version": 3,
    "icons": {
        "16": "images/icon-16.png",
        "32": "images/icon-32.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
    }"background": {
        "service_worker": "background.js", // 脚本路径
        "type": "module"
    },
    "content_scripts": [ 
        {
            "matches": [
                "<all_urls>"
            ],
            "css": [
                "content.css"
            ],
            "js": [
                "content.js"
            ],
            "run_at": "document_end"
        }
    ],
    "permissions": [
        "tabs",
        "activeTab"
    ],
}

可访问资源web_accessible_resources

使用 web_accessible_resources 清单属性声明向哪些来源公开哪些资源。此属性是声明资源访问规则的对象数组。每个对象都会列出一些扩展程序资源,并且必须为至少一个 matchesextension_ids 键提供一个值,以指明可以访问这些资源的来源。

{
    "name": "chrome-extension",
    "version": "1.0",
    "description": "chrome extension",
    "manifest_version": 3,
    "icons": {
        "16": "images/icon-16.png",
        "32": "images/icon-32.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
    }"background": {
        "service_worker": "background.js", // 脚本路径
        "type": "module"
    },
    "content_scripts": [ 
        {
            "matches": [
                "<all_urls>"
            ],
            "css": [
                "content.css"
            ],
            "js": [
                "content.js"
            ],
            "run_at": "document_end"
        }
    ],
    "permissions": [
        "tabs",
        "storage",
        "scripting",
        "activeTab"
    ],
    "web_accessible_resources": [
        {
            "resources": [
                "assets/*",
                "static/index.js"
            ],
            "matches": [
                "<all_urls>"
            ]
        }
    ],
}

通信

后台脚本和内容脚本之间的通信

发送方

background/popup

chrome.tabs.query({ active: true, currentWindow: true }, (result) => {
    const tab = result[0];
    chrome.tabs.sendMessage(tab.id, { message: 'start-panel' }, function (response) {
        if (response?.message === 'success') {
            window.close();
        }
    });
})

content_scripts

chrome.runtime.sendMessage({
    message: TO_CREATE_CHAT,
}, (response) => {
    console.log(response)
});

接收方

不管是background/popup/content_scripts接收事件是一样的

chrome.runtime.onMessage.addListener((request: any, sender: chrome.runtime.MessageSender, sendResponse: any) => {
    // 处理逻辑
});

侧边栏sidePanel

将内容托管在浏览器侧边栏中的网页主要内容旁边,侧边栏只能主动唤醒,具有一定的交互限制

{
    "name": "chrome-extension",
    "version": "1.0",
    "description": "chrome extension",
    "manifest_version": 3,
    "icons": {
        "16": "images/icon-16.png",
        "32": "images/icon-32.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
    }"background": {
        "service_worker": "background.js", // 脚本路径
        "type": "module"
    },
    "content_scripts": [ 
        {
            "matches": [
                "<all_urls>"
            ],
            "css": [
                "content.css"
            ],
            "js": [
                "content.js"
            ],
            "run_at": "document_end"
        }
    ],
    "permissions": [
        "tabs",
        "storage",
        "scripting",
        "activeTab",
        "sidePanel" // 添加权限
    ],
    "side_panel": {
        "default_path": "sidepanel.html" // 侧边栏页面
    }
    "web_accessible_resources": [
        {
            "resources": [
                "assets/*",
                "static/index.js"
            ],
            "matches": [
                "<all_urls>"
            ]
        }
    ],
    
}

存储storage

chrome.storage可以实现在插件中数据共享。需要在permissions中添加storage权限

一般有三种模式:

  1. chrome.storage.local

    数据会存储在本地,并会在移除扩展程序时清除。存储空间上限为 10 MB(在 Chrome 113 及更早版本中为 5 MB),但您可以通过请求 "unlimitedStorage" 权限提高上限。建议使用 storage.local 存储大量数据

  2. chrome.storage.sync

    如果启用同步,数据将同步到用户登录的任何 Chrome 浏览器。如果禁用,它的行为类似storage.local

  3. chr``ome.storage.session

    在浏览器会话期间将数据保存在内存中。默认情况下,它不会暴露给内容脚本,但可以通过设置更改此行为chrome.storage.session.setAccessLevel()。配额限制约为 10 MB

// 例子
chrome.storage.local.set({ key: value }).then(() => {
  console.log("Value is set");
});

chrome.storage.local.get(["key"]).then((result) => {
  console.log("Value is " + result.key);
});

chrome.storage.onChanged.addListener(
  callback: function,
)

加载与调试

加载插件

打开 Chrome 浏览器的扩展程序管理页面

  • 在 Chrome 浏览器中,点击右上角的三点菜单(更多),选择“更多工具” -> “扩展程序”。

  • 直接在地址栏输入 chrome://extensions/ 并按回车键。

启用开发者模式

  • 在扩展程序管理页面的右上角,找到并启用“开发者模式”开关。这将允许你加载未发布的插件和查看详细的调试信息。

加载插件

  • 点击“加载已解压的扩展程序”按钮。
  • 选择你创建的插件文件夹 my-chrome-extension,然后点击“选择文件夹”。
  • 此时,你的插件将被加载到 Chrome 浏览器中。你应该能在扩展程序管理页面中看到你的插件图标。

更新插件

  • 如果你对插件进行了修改,返回到扩展程序管理页面,点击“刷新”按钮来更新插件。

调试插件

调试后台脚本

  • 在扩展程序管理页面,点击Service Worker打开开发者工具。这就可以查看和调试后台脚本的日志、错误和其他信息。
  • 你可以在控制台标签页中查看日志消息,使用断点调试代码。

调试内容脚本

  • 内容脚本运行在网页的上下文中,因此需要打开相应的网页来调试它。
  • 在开发者工具中,切换到源代码标签页,找到内容脚本文件并设置断点。
  • 你可以在控制台标签页中查看内容脚本的日志和错误信息。

调试插件弹窗

  • 点击浏览器工具栏中的插件图标,打开插件弹窗。
  • 在插件弹窗打开的状态下,右键单击插件弹窗并选择检查打开开发者工具。
  • 在开发者工具中,你可以查看和调试插件弹窗的 HTMLCSS JavaScript文件。

结束语

以上是本人初学chrome插件的一些记录,希望对大家有所帮助,如果大家有疑问,欢迎留言讨论!