目前的工作一直和浏览器插件打交道,但是也没有很好的总结输出文章,此时chrome也在正全面项manifest v3升级,抛弃manifest v2,官方声称《2024 年 6 月:在稳定发布前弃用 Chrome MV2》
接下来我们来了解如何开发chrome MV3插件吧。
组成部分
清单 manifest.json
扩展程序的清单是唯一且必须具有特定文件名的必需文件:manifest.json。该文件还必须位于相应扩展程序的根目录下。该清单会记录重要元数据、定义资源、声明权限以及标识在后台和网页中运行的文件。
Service Worker
Service Worker 在后台运行并处理浏览器事件,例如移除书签或关闭标签页。它们无权访问 DOM,但针对这种使用情形,您可以将其与屏幕外文档结合起来。
内容脚本
内容脚本在网页环境中运行 JavaScript。
popup 工具栏弹出框
在用户点击扩展程序工具栏图标或使用 Action API 显示弹出式窗口时,执行代码。
开发一个常规的浏览器插件,了解这些部分就可以了。下面我们先来具体介绍一下manifest中的配置吧。
manifest
必须具备的属性(本地开发)
manifest_version // 用于指定扩展程序使用的清单文件格式版本,目前直接是3
name // 扩展程序的名称,长度 < 45字符
version // 当前扩展程序的版本号
上传浏览器扩展商店必须
manifest_version // 用于指定扩展程序使用的清单文件格式版本,目前直接是3
name // 扩展程序的名称,长度 < 45字符
version // 当前扩展程序的版本号
description // 用于描述 Chrome 应用店和用户的扩展程序管理页面上的扩展程序,长度 < 132字符
icons // 一个或多个代表您的扩展程序的图标。只会显示在应用商店,扩展程序列表处,不会出现在工具栏上
可选属性
action
定义扩展程序图标在 Google 工具栏中的外观和行为。
"action": {
// 图标
"default_icon": {
"16": "icon/icon16.png",
"48": "icon/icon48.png",
"128": "icon/icon128.png"
},
// 移入时提示的文字,不设置默认是扩展名称
"default_title": "Click Me"
}
注意如果不设置action属性,在Service Worker和popup脚本中使用chrome.action
将会报错。
我们还可以设置角标信息来表示一些状态,只不过只能通过编程的方式设置,不能在manifest中设置默认的。
还可以设置弹出窗口,他可以是任意的html结构,可以直接在manifest中配置action.default_popup
路径。内部可以引入自己的css, js等文件。js脚本的执行也是在popup页面中执行的。popup.html只可以引入外链脚本,内联脚本不可以执行,会报错。 但是可以直接在popup.html中写内部样式。
<head>
<title>manifest v3测试</title>
<script src="popup.js"></script>
<style>
#searchButton {
color: red;
}
</style>
</head>
<body>
<button id="searchButton">manifest v3测试</button>
</body>
background
指定包含扩展程序的 Service Worker(充当事件处理程序)的 JavaScript 文件。扩展 Service Worker 在需要时加载,并在其进入休眠状态时取消加载。扩展 Service Worker 无法访问 DOM。
如果background脚本使用了es6模块化,我们还需要配置type: module
。注意不能使用import()动态导入语法。
"background": {
"service_worker": "background.js",
"type": "module" // 使用es6模块化
},
// a.js
export const a = 1
// background.js
import {a} from "./a.js"
console.log("a", a)
我们也可以使用importScripts()
进行文件导入。
// a.js
const a = 1
// background.js
// 直接将代码导入到background中, 导入模块无需使用导出关键字
importScripts("./a.js") // undefined
console.log("a", a) // 1
content_scripts
指定在用户打开某些网页时要使用的 JavaScript 或 CSS 文件。内容脚本是在网页环境中运行的文件。主要是操作当前页面,然后和扩展通信。
"content_scripts": [
{
"matches": ["https://*/*", "http://*/*"], // 指定要将此内容脚本注入哪些网页。
"exclude_matches": ["*://*/*business*"], // 排除匹配的页面
"exclude_globs": ["*science*"], // 作用在matches规则后,在进行一次过滤。
"include_globs": ["*nytimes.com/???s/*"], // 作用在matches规则后,在进行一次过滤 。*表示匹配任意长度字符,?表示匹配单独字符
"css": ["./content/main.css"], // 要注入到匹配页面的 CSS 文件列表。这些元素会按照它们在此数组中出现的顺序进行注入
"js": ["./content/content.js"], // 要注入到匹配页面的 JavaScript 文件的列表。系统会按照文件在此数组中出现的顺序注入文件。 相对路径形式引入
"run_at": "document_idle", // document_idle dom构建完毕或者页面所有资源加载完毕后注入。 document_start css加载解析之后,构建dom和执行js脚本之前注入。 document_end dom构建完毕,资源未加载之前注入。
"all_frames": false, // 是否会注入到子frame中,注入子frame中也需要匹配matches规则,如果不匹配也不会注入。
"match_about_blank": false, // 是否注入到about:blank页面
"match_origin_as_fallback": false, // 脚本是否应注入由匹配的来源创建的页面。例如data:, blob:, about:blank等等,他会查看原网址来确定是否匹配,而不是通过当前网址去匹配matches。
"world": "ISOLATED" // "ISOLATED" 指定隔离环境,即此扩展程序独有的执行环境。"MAIN" 指定 DOM 的主域,即与托管网页的 JavaScript 共享的执行环境。
}
],
必须指定匹配规则,否则报错。
当内容脚本注入成功后,可以访问以下api。
-
storage
这个需要配置storage权限。 -
runtime.sendMessage()
内容脚本无法直接访问其他 API。 -
commands 定义扩展程序中的键盘快捷键。 注意,这里设置的快捷键不能和系统中或者其他插件快捷键冲突,否则后台和popup脚本将监听不到。不要以为是监听不到,闭坑。
"commands": {
"a": {
"suggested_key": {
"windows": "Alt+C",
"default": "Alt+C"
},
"description": "测试快捷键"
}
}
// popup / background监听
chrome.commands.onCommand.addListener((command) => {
console.log("command", command); // 这个就是设置的快捷键属性名 a
})
declarative_net_request
定义 declarativeNetRequest API 的静态规则,以允许拦截和修改网络请求。
V3将拦截修改网络请求apichrome.webRequest
更改为Declarative Net Request API
。就是我们可以通过配置来修改和屏蔽网络请求,而不是像v2一样去拦截请求做屏蔽和修改。
"permissions": ["declarativeNetRequest", "declarativeNetRequestFeedback"],
"declarative_net_request": {
// 配置资源规则
"rule_resources": [
{
"id": "ruleset_1",
"enabled": true,
// 具体操作项。这里也可以指定josn文件路径
"path": [
{
"id": 1,
"priority": 1,
// 操作行为
"action": {
// 修改请求头
"type": "modifyHeaders",
"requestHeaders": [{ "header": "cookie", "operation": "remove" }]
},
// 行为条件
"condition": {
// 匹配的主机。就是 webRequest 监听器的 urls 选项
"urlFilter": "|*?no-cookies=1",
// 指定要屏蔽的资源的类别
"resourceTypes": ["main_frame"]
}
}
]
}
]
}
export, import
允许从扩展程序导出和导入资源。
"export": {
// 设置当前扩展资源允许被导入的扩展id,不设置将表示任何扩展都可以导入
"allowlist": []
},
"import": [
{"id": "扩展id"},
{
"id": "扩展id"
"minimum_version": "0.5" // optional
}
]
// 访问资源时,可通过导入扩展程序根目录中的预留路径 _modules/SHARED_MODULE_ID 访问。
<script src="_modules/扩展id/资源路径">
externally_connectable
指定哪些其他页面和扩展程序可以连接到您的扩展程序。声明了哪些扩展程序和网页可以通过 runtime.connect 和 runtime.sendMessage 连接到您的扩展程序。
如果您未在扩展程序的清单中声明 externally_connectable
键,则所有扩展程序都可以连接,但任何网页都无法连接。** ****因此,在更新清单以使用 externally_connectable
时,如果未指定 "ids": ["*"]
,其他扩展程序将无法连接到您的扩展程序。这可能是意外的后果,因此请谨记。
"externally_connectable": {
// 指定允许连接的扩展程序的 ID。如果留空或未指定,则任何扩展程序或应用都无法连接。通配符 "*" 将允许所有扩展程序和应用连接。
"ids": [
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
],
// 指定允许连接的网页的网址格式。如果留空或未指定,则任何网页都无法连接。格式不能包含通配符网域,也不能包含(有效)顶级域名的子网域
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*"
],
// 允许扩展程序使用与其连接的网页的 TLS 通道 ID。
"accepts_tls_channel_id": false
},
options_page, options_ui
选项界面中只能引入外部脚本,不能在html中设置内联脚本。
- options_page 指定 options.html 文件的路径,以将扩展程序用作选项页面。
options_page: "options.html" // 单独一个tab页面作为选项界面
- options_ui 指定 HTML 文件的路径,该文件允许用户在 Chrome 扩展程序页面更改扩展程序选项。他主要是配置详细的选项配置。
"options_ui": {
"page": "options.html", // 指定选项页面相对于扩展程序根目录的路径。
"open_in_tab": false // 用于指示是否在新标签页中打开扩展程序的选项页面。如果设为 `false`,该扩展程序的选项页面会嵌入 `chrome://extensions` 中,而不会在新标签页中打开。
},
host_permissions, optional_host_permissions
- host_permissions 字段用于指定扩展程序需要访问的特定主机或URL模式。这与普通的权限不同,它更为细粒度,允许扩展程序只在特定的网站上执行操作。
- optional_host_permissions 字段用于列出扩展程序可能需要访问的特定主机或URL模式,但用户可以选择是否授予这些权限。
// 点击按钮询问扩展是否可以访问该网站
const PERMISSIONS = {origins: ['http://api.stackoverflow.com/']};
document.querySelector('button#enable').addEventListener('click', function() {
chrome.permissions.contains(PERMISSIONS, function(allowed) { // 先判断是否包含该主机的访问权限
if (allowed) {
alert('You already have SO host permission!');
} else {
chrome.permissions.request(PERMISSIONS, function(result) {
if (result) {
console.log('SO host permission granted!' +
'Open the browser action again.');
}
});
}
});
});
permissions , optional_permissions
- permissions 指定权限列表,让扩展中可以使用特定的扩展程序 API。
- optional_permissions 字段用于列出扩展程序可能需要的权限,但用户可以选择是否授予这些权限(这个确认的操作是我们使用代码控制的,而不是安装插件就会询问。)。用户安装扩展程序时,将被提示是否同意这些可选权限。
// 我们点击按钮去触发询问是否授予权限。
const newPerms = {
permissions: ['topSites']
};
const button = document.createElement('button');
button.innerText = 'Allow Extension to Access Top Sites';
button.addEventListener('click', () => {
chrome.permissions.request(newPerms).then((granted) => {
if (granted) {
console.log('granted');
sites_div.innerText = '';
createTop();
} else {
console.log('not granted');
}
});
});
必需权限的优点:
- 减少提示:扩展程序可以提示用户接受所有权限一次,
- 开发更简单:必要权限必定存在。
可选权限的优点:
- 安全性更高:由于用户仅启用需要的权限,因此扩展程序运行时的权限会更少。
- 为用户提供更优质的信息:在用户启用相关功能时,扩展程序可以解释为什么需要特定权限。
- 升级更轻松:升级扩展程序时,如果升级添加了可选权限而非必需权限,Chrome 不会为用户停用该扩展程序。
web_accessible_resources
定义扩展程序中可供网页或其他扩展程序访问的文件。每个元素都必须包含一个 "resources"
元素以及一个 "matches"
或 "extension_ids"
元素。
{
"web_accessible_resources": [
{
"resources": [ // 每个字符串都包含从扩展程序根目录到给定资源的相对路径。
"images/*"
],
"matches": [ // 用于指定哪些网站可以访问这组资源。
"*://*/*"
]
},
{
"resources": [
"style/extension.css",
"script/extension.js"
],
"extension_ids": [ // 每个字符串都包含可以访问资源的扩展程序的 ID。
"id"
],
"use_dynamic_url": false // 如果为 true,则仅允许通过动态 ID 访问资源。系统会为每个会话生成一个动态 ID。这意味着,当浏览器重启或扩展程序重新加载时,系统会重新生成该文件。
}
]
}
我们暴露出当前扩展的图片资源,在给定权限的网站中引入就可以访问。引入的路径chrome-extension://extensionId/资源名称
。
chrome_url_overrides
定义默认 Chrome 网页的替换项,比如替换新标签页(chrome://newtab),书签页(chrome://bookmarks),历史记录页(chrome://history)。
{
"name": "替换页面",
"manifest_version": 3,
"version": "1.0",
"incognito": "not_allowed",
"chrome_url_overrides": {
"newtab": "index.html"
}
}
<input type="text">
替换页面....
其他
author // 指定用于创建扩展程序的帐号的电子邮件地址。
chrome_settings_overrides // 定义所选 Chrome 设置的替换项。
content_security_policy // 定义对扩展程序可以使用的脚本、样式和其他资源的限制。
cross_origin_embedder_policy // 指定 Cross-Origin-Embedder-Policy HTTP 标头的值,该标头用于配置在扩展程序页面中嵌入跨源资源。
cross_origin_opener_policy // 指定 Cross-Origin-Opener-Policy HTTP 标头的值,可让您确保顶级扩展程序页面不会与跨源文档共享浏览上下文组。
devtools_page // 定义使用 [DevTools](https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools?hl=zh-cn) API 的页面。
default_locale // 用于定义支持多个语言区域的扩展程序的默认语言。
homepage_url // 用于指定扩展程序首页的网址。一般是代码仓库和官方网站
incognito // 定义扩展程序在无痕模式下的行为。支持的值包括 `"spanning"`、`"split"` 和 `"not_allowed"`。
key // 为各种开发用例指定扩展程序的 ID。
minimum_chrome_version // 定义可安装扩展程序的最低 Chrome 版本。该值必须是现有 Chrome 浏览器版本字符串的子字符串,例如 `"107"` 或 `"107.0.5304.87"`。
oauth2 // 允许使用 OAuth 2.0 安全 ID。此键的值必须是具有 `"client_id"` 和 `"scopes"` 属性的对象。
omnibox // 允许此扩展程序在 Chrome 的地址栏中注册关键字。类似于地址栏输入一些网站的快捷方式。
requirements // 列出使用扩展程序所需的技术。
sandbox // 定义一组扩展程序页面,它们无权访问扩展程序 API 或直接访问非沙盒化页面。
short_name // 包含要在字符空间有限时使用的扩展程序名称的缩写版本。长度上限为 12 个字符。如果未定义,将显示“name”键的截断版本。
side_panel // 标识要在 [sidePanel](https://developer.chrome.com/docs/extensions/reference/api/sidePanel?hl=zh-cn) 中显示的 HTML 文件。
tts_engine // 将扩展程序注册为文字转语音引擎。
update_url // 包含扩展程序的更新页面的网址。
version_name // 一个描述扩展程序版本的字符串。
storage // 声明[托管存储区域](https://developer.chrome.com/docs/extensions/reference/storage?hl=zh-cn#property-managed)的 JSON 架构。
以上就是大致梳理了一下chrome MV3中的一些配置,关键配置都已经详细介绍,如果开发中遇到问题,可以再去查阅官方文档。
参考
往期年度总结
往期文章
- 反调试吗?如何监听devtools的打开与关闭
- 因为原生,选择一家公司(前端如何防笔试作弊)
- 结合开发,带你熟悉package.json与tsconfig.json配置
- 如何优雅的在项目中使用echarts
- 如何优雅的做项目国际化
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )