Chrome插件开发入门教程

Chrome 插件由不同的部件构造而成,包括:后台脚本、内容脚本、选项页、UI 元素以及各种逻辑文件。这些插件部件都是使用 Web 技术来开发的,只要掌握 HTMLCSSJavaScript 即可上手开发。

一个插件需要包含哪些部件是由插件功能决定的,并非每种部件都是必要的。

本节我们将从零开始开发一个 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 )即可完成加载,关键步骤如下:

  1. 打开插件管理页面,直接点这个访问这个链接即可:chrome://extensions
    • 或者从菜单进入 设置页 ,找到并点击 扩展程序
    • 亦或从菜单移到 更多工具 ,找到并点击 扩展程序
  2. 点击位于右上角的 开发模式 开关,开启开发模式;
  3. 点击左上角的 加载已解压的扩展程序 ,然后直接选择插件目录即可;

插件安装成功!虽然它现在还什么都干不了,但总算迈出了第一步。由于我们还没在 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.storageundefined ,因为 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插件开发】系列文章首发于公众号【小菜学编程】,敬请关注:

【Chrome插件开发】系列文章首发于公众号【小菜学编程】,敬请关注: