Chrome 插件由不同的部件构造而成,包括:后台脚本、内容脚本、选项页、UI 元素以及各种逻辑文件。这些插件部件都是使用 Web 技术来开发的,只要掌握 HTML 、CSS 和 JavaScript 即可上手开发。
一个插件需要包含哪些部件是由插件功能决定的,并非每种部件都是必要的。
本节我们将从零开始开发一个 Chrome 插件,让用户可以改变当前网页的背景色。开发这个插件涉及 Chrome 插件平台的诸多部件,借此可更加深入地理解各种部件间的关系。
开始之前,我们先创建一个目录,用来保存插件文件:
1
2
|
$ mkdir coloring
$ cd coloring
|
manifest
插件由 manifest 定义,因此需要先在插件目录下创建 manifest.json
文件,内容如下:
1
2
3
4
5
6
|
{
"name": "Coloring",
"description": "Demo Extension to change background color!",
"version": "1.0",
"manifest_version": 3
}
|
manifest 描述插件的元数据,包括插件名、描述以及版本等等。
加载未打包插件
为方便插件调试,Chrome 在开发模式下,支持加载未打包的插件。只需指定插件的开发目录(包含 manifest )即可完成加载,关键步骤如下:
- 打开插件管理页面,直接点这个访问这个链接即可:chrome://extensions ;
- 或者从菜单进入 设置页 ,找到并点击 扩展程序 ;
- 亦或从菜单移到 更多工具 ,找到并点击 扩展程序 ;
- 点击位于右上角的 开发模式 开关,开启开发模式;
- 点击左上角的 加载已解压的扩展程序 ,然后直接选择插件目录即可;
插件安装成功!虽然它现在还什么都干不了,但总算迈出了第一步。由于我们还没在 manifest 配置文件里注册图标,Chrome 会为插件显示默认图标。
添加功能
插件虽已安装,却啥也没干,因为还没添加执行逻辑。我们先写一些代码,让插件保存背景颜色值。
注册后台脚本
跟其他重要部件一样,后台脚本也必须注册到 manifest 配置文件。在 manifest 注册后台脚本,相当于告诉插件去哪找后台脚本,以及脚本应该执行什么事情。
1
2
3
4
5
6
7
8
9
10
|
{
"name": "Coloring",
"description": "Demo Extension to change background color!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
}
}
|
在 manifest 加上 background 配置后,Chrome 现在知道插件包含一个 服务执行器( service worker )。当你重新加载插件时,Chrome 会找到注册的后台脚本,执行其中的额外指令,比如监听一些重要事件。
编写后台脚本
现在终于可以开始编写后台脚本了,在插件目录创建 background.js 文件,并写上代码:
1
2
3
4
5
6
|
const color = "#3aa757";
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.sync.set({ color });
console.log(`[Coloring] default background color is set to: ${color}`);
});
|
这段代码通过常量定义默认的背景色,然后通过 Chrome API 注册 onInstalled 事件监听函数。监听函数将在插件安装的时候执行,调用 storage 存储接口,保存默认的背景色。
之所以用 storage 来保存背景色,是为了能够在不同的部件中访问或者更新它。
现在点击插件右下角的转圈,重新加载插件,但我们发现插件报错了。因为插件使用了 storage 接口,但还没获得 Chrome 的授权。
检查后台脚本
点击重新加载插件后,插件卡片会显示 查看视图 新字段,包含一个可以点击的链接 Service Worker 。点击该链接,即可打开对应后台脚本的调试终端,从中可以查到报错细节:
由此可见,chrome.storage 是 undefined ,因为 Chrome 还没将 storage 接口授权给插件。
存储接口授权
大部分接口,包括 storage 接口,必须注册在 manifest 配置文件 permissions 字段下,获得授权之后才能使用:
1
2
3
4
5
6
7
8
9
10
|
{
"name": "Coloring",
"description": "Demo Extension to change background color!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["storage"]
}
|
重新加载插件,可以看到 background.js 执行无误,并如预期那样输出日志。
引入用户界面
Chrome 插件可能使用各种形式的用户界面,我们先试试 Popup 弹出窗口。在插件目录创建一个名为 popup.html
的文件,并添加一个按钮来改变背景色:
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="button.css">
</head>
<body>
<button id="changeColor"></button>
</body>
</html>
|
Chrome 插件用户界面也是用 HTML 来编写,前端工程师应该再熟悉不过了。
跟后台脚本一样,用户界面文件也要在 manifest 中注册后,才能被 Chrome 识别。我们在 manifest 中添加一个 action 对象,并将 popup.html 文件作为 default_popup 属性注册进去:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
{
"name": "Coloring",
"description": "Demo Extension to change background color!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["storage"],
"action": {
"default_popup": "popup.html"
}
}
|
注意到,HTML 中引用了一个外部 CSS 文件来控制按钮的样式,名为 button.css 。同样,我们在插件目录下创建该文件,并写上以下样式代码:
1
2
3
4
5
6
7
8
9
10
11
12
|
button {
height: 30px;
width: 30px;
outline: none;
margin: 10px;
border: none;
border-radius: 2px;
}
button.current {
box-shadow: 0 0 0 2px white, 0 0 0 4px black;
}
|
现在插件还在使用默认图标,辨识度不高,是时候给它加上个性化图标了。图标图片为正方形,宽高必须一致,而且必须提供多种不同尺寸。
我们从 Chrome 官方文档中下载了一组图标作为素材,讲解如何为插件加上个性化图标。这些图标可从本插件的代码仓库中获得,Github 地址是:https://github.com/coding-fans/chrome-coloring-extension 。
插件在工具栏上显示的图标,也由 action 对象指定,具体为 default_icon 字段。插件在管理页面、权限警示框等场景也会显示图标,可以通过 manifest 配置 icons 字段指定(跟 action 同级)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
{
"name": "Coloring",
"description": "Demo Extension to change background color!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["storage"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/get_started16.png",
"32": "images/get_started32.png",
"48": "images/get_started48.png",
"128": "images/get_started128.png"
}
},
"icons": {
"16": "images/get_started16.png",
"32": "images/get_started32.png",
"48": "images/get_started48.png",
"128": "images/get_started128.png"
}
}
|
重新加载插件后,我们看到图标已经生效了。点击固定按钮,还能将插件固定到工具栏:
现在点击插件图标,会弹出一个按钮,它正是我们在 popup.html 中定义的:
我们希望按钮跟插件保存的背景色一样,点击它就将当前网页背景色改掉。为此,我们先创建 popup.js 脚本,为按钮加上颜色,代码如下:
1
2
3
4
5
6
7
|
// 通过ID找到按钮
const button = document.getElementById("changeColor");
// 从storage取背景色并设到按钮上
chrome.storage.sync.get("color", ({ color }) => {
button.style.backgroundColor = color;
});
|
代码写好后,我们需要在 popup.html 中通过 script 标签引入它:
1
2
3
4
5
6
7
8
9
10
|
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="button.css">
</head>
<body>
<button id="changeColor"></button>
<script src="popup.js"></script>
</body>
</html>
|
重新加载插件,可以看到弹出来的按钮已经变成绿色的了。
操作逻辑
插件已经初具雏形,它有自己的图标,有一个弹框按钮,颜色跟保存在 storage 中的背景色一样。我们希望点击按钮就能将当前网页的背景色改掉,控制逻辑同样在 popup.js 中实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 注册按钮点击回调函数
button.addEventListener("click", async () => {
// 调用Chrome接口取出当前标签页
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
// 以当前标签页为上下文,执行setPageBackgroundColor函数
chome.scripting.executeScript({
target: {tabId: tab.id},
function: setPageBackgroundColor,
});
});
// 函数将在指定标签页内执行,因此可以取得当前网页document
function setPageBackgroundColor() {
// 从storage取出背景色,并设到当前网页上
chrome.storage.sync.get("color", ({ color }) => {
document.body.style.backgroundColor = color;
});
}
|
这段代码为按钮注册点击事件回调函数,按钮被点击时 Chrome 将执行该函数。回调函数先调用 Chrome 接口获取当前标签页,然后以当前标签页为上下文执行 setPageBackgroundColor 函数。
setPageBackgroundColor 函数先从 storage 中取出背景色,然后设为当前网页的背景色。由于函数以当前标签页为上下文,因此可以通过 document 直接定位到当前网页。
重新加载插件,但我们发现按钮点击后网页背景色并没有按预想那样改变。这时,右键点击弹出按钮,然后点击检查即可查看报错信息。
同样,插件还没取得在标签页中执行脚本的权限,我们需要在 manifest 申请权限:
1
2
3
4
5
6
|
{
"name": "Coloring",
...
"permissions": ["storage", "activeTab", "scripting"],
...
}
|
现在插件的功能已经完整了!重新加载插件,按下弹出按钮即可将当前网页背景色改为绿色。然而,用户可能想改成其他颜色,因此我们还得开发选项配置功能。
请注意,插件通过给 body 标签设置样式来修改网页背景色。因此,如果网页背景色是由其他标签决定的,便无法生效。
选项配置
插件现在只支持将背景色设为绿色,这样很不灵活。我们可以给插件添加选项页,让用户更好的控制插件的功能,比如更改背景色,这样使用体验更好。
选项页同样由 HTML 实现,在插件目录下创建 options.html 文件,并加上以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="button.css">
</head>
<body>
<div id="buttonDiv">
</div>
<div>
<p>Choose a different background color!</p>
</div>
<script src="options.js"></script>
</body>
</html>
|
同样,选项页需要在 manifest 配置中注册,Chrome 才能识别:
1
2
3
4
5
|
{
"name": "Coloring",
...
"options_page": "options.html"
}
|
这时重新加载插件,右键点击插件图标,可以看到右键菜单中的 选项 菜单,点它就会打开我们添加的选项页 options.html 。点击 管理扩展程序 ,然后拉到最后也可以找到选项入口。
当然了,现在的选项页还无法正常工作,因为我们还没给它添加处理逻辑。您可能已经发现了,选项页 options.html 中也已用了一个 JavaScript 脚本 options.js ,亟待实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
/*
* Author: fasion
* Created time: 2022-08-25 14:15:10
* Last Modified by: fasion
* Last Modified time: 2022-08-25 14:50:11
*/
// 放置颜色选择按钮的容器
const buttonContainer = document.getElementById("buttonDiv");
// 当前选中按钮的类名
const selectedClassName = "current";
// 可供选择的颜色
const presetButtonColors = ["#3aa757", "#e8453c", "#f9bb2d", "#4688f1"];
// 按钮点击回调函数
function handleButtonClick(event) {
// 根据类名找出上次点击的按钮
let last = event.target.parentElement.querySelector(
`.${selectedClassName}`
);
// 如果上次点击按钮跟本次点击一样,无须处理
if (last && last === event.target) {
return;
}
// 取出被点击按钮对应的颜色,并保存到storage
const color = event.target.dataset.color;
chrome.storage.sync.set({ color });
// 将表示点击状态的类名,从上次按钮移除,并添加到本次按钮
last.classList.remove(selectedClassName);
event.target.classList.add(selectedClassName);
}
// 为每种颜色添加一个按钮
function constructOptions(buttonColors) {
// 从storage中取出当前保存的背景色
chrome.storage.sync.get("color", (data) => {
const currentColor = data.color;
// 遍历每种可供选择的颜色
for (const buttonColor of buttonColors) {
// 创建一个按钮节点
const button = document.createElement("button");
// 设置按钮颜色,并保存颜色值
button.style.backgroundColor = buttonColor;
button.dataset.color = buttonColor;
// 如果按钮颜色刚好是当前保存的背景色,加上类名表示选择
if (buttonColor === currentColor) {
button.classList.add(selectedClassName);
}
// 为按钮添加回调函数,点击按钮就执行上一个函数
button.addEventListener("click", handleButtonClick);
// 将按钮加到div容器
buttonContainer.appendChild(button);
}
});
}
// 执行函数生成颜色选择按钮
constructOptions(presetButtonColors);
|
这段代码定义了四种可供选择的颜色,并在选项页中生成四个按钮,点击按钮就将对应颜色保存到 storage 。现在插件已经像模像样了,用户能够自定义背景色了!
总结
经过本节学习,我们成功开发了一个简单但功能完整的 Chrome 插件,掌握了插件开发的基本思路。如果向更加深入地了解 Chrome 插件开发,可以参考以下资料:
【Chrome插件开发】系列文章首发于公众号【小菜学编程】,敬请关注: