Chrome扩展开发指南

前言

Chrome 扩展(通常也叫插件)也是软件程序,使用 Web(HTML, CSS, and JavaScript)技术栈开发。允许用户自定义 Chrome 浏览体验。开发者可以通过增加特效或功能来优化体验。例如:效率工具、信息聚合等等。你可以在 Chrome 应用商店 查看各种各样的扩展。比如一些翻译扩展、JSON 格式化扩展等等都极大提高了我们开发或工作效率。本文旨在帮助大家了解 Chrome 扩展开发的基本概念、开发流程。并最终开发一个背景颜色提取的扩展来加深 Chrome 扩展的印象。本文基于 Manifest V3,大部分内容翻译自https://developer.chrome.com/docs/extensions/mv3/。最终我们要实现的扩展功能如下:

98c90306f2e5830a8d0b6026d4f99a59.gif

基本概念

Manifest

扩展的 manifest 是唯一必须的文件,且文件名必须是 manifest.json。manifest 必须放在扩展程序的根目录,它记录了重要的 metadata、资源定义、权限声明并标识了哪些文件运行在后台和页面上。

Service Worker

扩展的 Service Worker 用户处理和监听 浏览器 事件。比如跳转到一个新页面、书签移除、tab 关闭等。它可以使用所有的 Chrome API,但是不能直接和网页交互(和网页交互需要 Content Scripts)。

Content Scripts

Content Script 可以在网页上下文中执行 JavaScript。也可以读取和修改它们注入页面的 DOM。Content Script只能使用部分 Chrome API。其余的可以通过 Service Worker 间接访问。

Popup 和 Pages

扩展可以包含多种 HTML 文件,比如弹窗、页面选项等,这些页面都可以访问 Chrome API。

开发一个简单扩展

下面我们来开发一个 Hello Extensions 的简单扩展

创建 manifest.json 文件

创建一个新的目录,在目录下创建一个名为 manifest.json 的文件。

mkdir hello_extension
cd hello_extension
touch manifest.json

manifest.json 中加入以下代码:

{
  "manifest_version": 3,
  "name": "Hello Extensions",
  "description": "Base Level Extension",
  "version": "1.0",
  "action": {
    "default_popup": "hello.html",
    "default_icon": "hello_extensions.png"
  }
}

上面的 JSON 文件描述了扩展的功能和配置,比如 action 中描述了在 Chrome 中需要显示的扩展的图标以及点击图标后弹出的页面。可以在这里 下载 图标到你的目录下,然后把名字改为 manifest.json 中配置的 default_icon

创建 html 文件

上面的 action 中还配置了一个 default_popup,接下来我们创建一个名为 hello.html 的文件并加入如下代码。

<html>
  <body>
    <h1>Hello Extensions</h1>
  </body>
</html>

加载扩展

在开发模式下加载未打包的扩展。

  • 在 Chrome 浏览器地址栏中输入 chrome://extensions。也可以通过下图的两种方式打开。

3fe8eae40e38e5edb14b740297592713.pnga1aa543d6406465695deac54cdb993ef.png

  • 打开开发者模式

d4b82e1fbfd9c5497f04e60ec95c7718.png
image.png
  • 点击加载已解压的扩展程序

9e334e86b2a212b9cd9fb2120ef3888e.png选择我们的文件夹43858a3696f641488669864c2d2edc31.png完成之后即可在扩展页面显示出来9564deb1ca2ae7acfb003ffbd552167a.png

固定扩展

261ba3dd6c0b867895bd5f40a1d4a113.png点击固定,我们的扩展就可以在工具栏显示了。然后我们点击 icon,就可以弹出对应的页面了。2a076dc4c14ff58f0295cfb22abe03a7.png

重新加载扩展

我们回到代码,修改扩展的名字为 “Hello Extensions of the world!”

{
  "manifest_version": 3,
  "name": "Hello Extensions of the world!",
  ...
}

保存之后,回到扩展页面,点击刷新,可以看到,名字已经改变。72a56dc28107d94534eb6a2c83c4c8f5.png那么每次改动都需要重新加载扩展吗?可以参考下表。

扩展组件 是否需要重新加载
The manifest Yes
Service worker Yes
Content Scripts Yes (plus the host page)
The popup No
Options page No
Other extension HTML pages No

查看控制台日志以及错误

Console logs

在开发过程中,你可以通过浏览器控制台日志调试代码。在之前的代码中加入 <script src="popup.js"></script>

<html>
  <body>
    <h1>Hello Extensions</h1>
    <script src="popup.js"></script>
  </body>
</html>

创建一个名为 popup.js 的文件并加入如下代码:

console.log("This is a popup!")

接下来:

  1. 刷新扩展

  2. 打开popup

  3. 右键popup

  4. 选择 Inspect(检查)

719c513b73bd86f64a3296e98427434e.png
image.png
  1. 在开发者工具中切换到Console tab。

c3e12073221f2d325d613c9438e79ec6.png
image.png

Error logs

下面我们修改 popup.js,去掉一个引号

console.log("This is a popup!) // ❌ 错误代码

刷新之后,然后再点击打开扩展,可以看到错误按钮出现。20cc4fb8ecca0db7e5b6d9d9d0c28d1b.png点击错误按钮,可以查看具体错误信息。2197de4416e564902b423ea9e16da634.png

扩展工程结构

扩展工程结构可以有多种方式,但是 manifest.json 文件必须位于根目录,下面是一种结构实例。84cb5d0b359ed5520ed2be23cc211acf.png

Content Script

Content Script 运行在网页的上下文环境中,通过标准的 DOM,Content Script 可以读取浏览器访问页面的细节,也可以对其进行修改,还可以把这些信息传递给父扩展。

理解Content Script的能力

Content Script可以直接使用一部分 chrome API,比如:

  • i18n

  • storage

  • runtime:

    • connect

    • getManifest

    • getURL

    • id

    • onConnect

    • onMessage

    • sendMessage

使用其他的可以通过发送消息的方式实现。Content Script可以在将扩展里的文件声明为 Web Accessible Resources 后访问这些文件。 Content Script 运行在一个独立的环境里,它可以对其所在的 JavaScript 环境进行修改,而不与页面或其他扩展的 Content Script 发生冲突。

<html>
  <button id="mybutton">click me</button>
  <script>
    var greeting = "hello, ";
    var button = document.getElementById("mybutton");
    button.person_name = "Bob";
    button.addEventListener(
      "click",
      () => alert(greeting + button.person_name + "."),
      false
    );
  </script>
</html>

ba3234c89de0c488dfc17c73c19fb20c.png0c2d0da5fe7d501454b47a9b20095f1a.png

Content Script 注入

Content Script 注入有三种方式,分别是静态声明、动态声明、编程注入。 静态声明比较简单,只需要在 manifest.json 文件的 content_scripts进行配置即可。注意需要指定 matches 字段以指明需要再哪些页面运行 Content Script 脚本。

"content_scripts": [
   {
     "matches": ["https://*.google.com/*"],
     "css": ["styles.css"],
     "js": ["content-script.js"]
   }
 ],

动态声明主要使用场景是 matches 不明确时使用。编程注入是需要响应事件或一些特定场合使用,我们最终的例子使用的是静态声明。

Service Worker

Chrome 扩展是基于事件的程序,用于修改或增强 Chrome 浏览器的浏览体验。事件是由浏览器触发的,比如导航到一个新页面,删除一个书签或关闭一个标签(tab)。扩展通过 Service Worker 中的脚本监听这些事件。然后执行特定的指令。 Service Worker 需要时被加载,休眠时被卸载。比如:

  • 扩展第一次安装或更新到一个新的版本

  • 一个扩展事件被触发

  • Content Script 或其他扩展发送了消息

Service Worker 被加载后,只要有事件 Service Worker 就会保持在运行状态。一旦空闲了30秒,浏览器就会停止它。 Service Worker 保持休眠状态,直到有它监听的事件发生,这时它会执行相应的事件监听器,然后空闲、卸载。

Service Worker 的日志在这里查看19e56e2761b4c33bb2b04a3b02ebef86.png

注册Service Worker

"background": {
    "service_worker": "background.js"
  },

初始化扩展

chrome.runtime.onInstalled.addListener(function (details) {
  console.log("onInstalled event has been triggered with details: ", details);
  // 检查安装、更新或卸载的原因
  if (details.reason == "install") {
    // 在安装扩展时执行的代码
  } else if (details.reason == "update") {
    // 在更新扩展时执行的代码
  } else if (details.reason == "uninstall") {
    // 在卸载扩展时执行的代码
  }
});
ca38136c4296540c0a0cef9d97ae230b.png
image.png

设置监听

/**
 * 注意需要声明权限
 */
chrome.bookmarks.onCreated.addListener(function (id, bookmark) {
  console.log(
    "Bookmark created with title: " +
      bookmark.title +
      " and url: " +
      bookmark.url
  );
});
ea660ba23d3154545797c38ea00bde84.png
image.png

过滤事件

/**
 * 注意需要声明 webNavigation 权限
 */
chrome.webNavigation.onCompleted.addListener(() => {
  console.info("The user has loaded my favorite website!");
});

bd6e18bd96754e5e7ae4b5ae6fe0be39.png未过滤时所有的事件都会打印。不同的事件对应不同的功能,需要选择合适的事件来进行监听。

Service Worker 与 Content Script 通信

点击扩展 tab 的事件我们可以通过 chrome.tabs.sendMessage,并在Content Script中注册 chrome.runtime.onMessage.addListener 接收。

颜色提取扩展开发

基于上面的知识点,我们要实现如下功能: 1、导航栏点击扩展按钮弹出一个显示详情的面板 2、在当前页面鼠标点击页面的元素,在弹出的面板上显示对应的背景色 3、点击复制按钮可以复制当前元素背景色

这里我们只列出核心代码如下:

manifet 配置

{
  "manifest_version": 3,
  "name": "EasyColor",
  "description": "Chrome extension for obtaining color in an easy way",
  "version": "0.0.1",
  "action": {
    "default_icon": "images/icon-48.png"
  },
  "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"
  },
  "content_scripts": [
    {
      "matches": ["http://*/*", "https://*/*"],
      "css": ["css/styles.css"],
      "js": ["scripts/content.js"],
      "run_at": "document_start"
    }
  ],
  "web_accessible_resources": [
    {
      "resources": ["/pages/panel.html"],
      "matches": ["http://*/*", "https://*/*"]
    }
  ]
}

导航栏点击扩展按钮弹出一个显示面板

首先在 service_worker 中,我们定义一个 background.js,用于监听扩展点击事件,并发送给 content_scripts。

chrome.action.onClicked.addListener(function (tab) {
  //open pages
  chrome.tabs.sendMessage(tab.id, {
    action: "EVENT_PANEL_OPEN",
  });
});

在 content_scripts 中监听 EVENT_PANEL_OPEN 事件并在当前页面增加一个iframe用来显示面板。

chrome.runtime.onMessage.addListener((req, sender, sendResp) => {
  const data = req;
  if (data.action === "EVENT_PANEL_OPEN") {
    let easyPanel = document.getElementById(easyPanelId);
    if (easyPanel == null) {
      easyPanel = document.createElement("iframe");
      easyPanel.id = easyPanelId;
      easyPanel.src = chrome.runtime.getURL("../pages/panel.html");
      easyPanel.style.width = "100%";
      easyPanel.style.height = "100%";
      easyPanel.style.borderRadius = "20px";
      easyPanel.style.border = "none";

      const container = document.createElement("div");
      container.id = easyContainerId;
      container.style.width = "200px";
      container.style.height = "250px";
      container.style.position = "fixed";
      container.style.top = "10px";
      container.style.right = "10px";
      container.style.zIndex = "10000";
      container.style.boxShadow = "3px 2px 22px 1px rgba(0, 0, 0, 0.24)";
      container.style.borderRadius = "20px";
      container.appendChild(easyPanel);

      document.body.appendChild(container);
    }

    document.addEventListener("mousemove", handleMouseMove);

    document.addEventListener(
      "click",
      function (e) {
        e.stopPropagation();
        e.preventDefault();
        return false;
      },
      true
    );
  }
});

点击显示对应颜色

在 content_scripts 中加入点击事件,获取当前元素背景颜色,然后传递给 iframe 进行显示。

document.addEventListener("mousedown", function (e) {
  let easyPanel = document.getElementById(easyPanelId);
  if (easyPanel == null) {
    return;
  }
  const cpStyle = window.getComputedStyle(e.target);
  easyPanel.contentWindow.postMessage(
    {
      type: "computedStyle",
      data: JSON.stringify({
        color: cpStyle.color,
        backgroundColor: cpStyle.backgroundColor,
      }),
    },
    "*"
  );
});

以上就是我们实现的元素颜色选择插件的核心代码,更多细节可以参考:https://github.com/yangpeng7/ChromeExtensionBestPractice

参考资源

https://developer.chrome.com/docs/extensions/mv3/

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

ac7d6df2700b8103827b195d9d66ebe9.png

猜你喜欢

转载自blog.csdn.net/qiwoo_weekly/article/details/130234301
今日推荐