一份关于Chrome插件开发指北

目前开发v2版本开发教程可看这里
但目前谷歌浏览器强制要v3,本文主要是总结一些v3跟v2的不同。

为什么迁移到清单V3?

正如Chrome的文档所说:

使用MV3的扩展程序将在安全性、隐私性和性能方面得到增强;它们还可以使用MV3中采用的更现代的开放网络技术,如服务人员和承诺。

manifest.json的更改

更改版本

首先明显的一步是您需要更改清单的版本。在manifest.json文件中,如下更改:

{
    ...,"manifest_version": 3,
    ...
}

如果您现在尝试将扩展程序添加到chrome(或如果已经存在则重新加载它),您会看到有关仍需对manifest.json文件进行的更改的不同错误。

主机权限

在清单V2中,从扩展程序向API或任何主机发出请求有两种方式:要么在permissions数组中,要么在optional_permissions数组中。

在清单V3中,所有主机权限现在都单独存在于一个新的数组中,键为host_permissions。主机权限不应再与其他权限一起添加。

回到我们的示例,这是我们的permissions数组:

{
    ...,"permissions": ["https://some-random-api.ml/*"],
    ...
}

现在,它应该更改为:

{
    ...,"host_permissions": ["https://some-random-api.ml/*"],
    ...
}

在我们的用例中,我们只需要将键从permissions更改为host_permissions。但是,如果您的扩展程序在permissions中有其他值,则应将主机权限保留在其中,并将主机权限移至host_permissions

后台脚本

清单V3使用服务人员替换背景脚本。我们稍后会讨论如何进行转换,但首先需要在manifest.json中进行转换。

我们的扩展程序中的background对象目前如下所示:

{
    ...,"background": {
        "scripts": ["assets/js/background.js"],
        "persistent": false
    },
    ...
}

我们需要做的就是将scripts数组键更改为service_worker,现在您应该只有一个服务人员而不是多个背景页面或脚本。所以,它应该是这样的:

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

请注意,我们不需要再添加persistent。另外,如果您在background中有page,则也应将其更改为服务人员。

操作

操作过去是browser_actionpage_action,但现在在清单V3中统一为action。这是因为随着时间的推移,它们变得相似,分离它们变得不必要。

我们在扩展中没有使用它,但这是一个它应该如何使用的示例:

{
    ...,"action": {
        //包括browser_action中的所有内容
        //包括page_action中的所有内容
    },
    ...
}

代码中也需要进行更改,我们稍后会讨论。

内容安全策略

同样,这没有在我们的扩展程序中使用,但我们仍然需要讨论它。 如果您的扩展程序具有内容安全策略(CSP),则需要将其从字符串(V2中的方式)更改为对象(V3中的方式)。

它在清单V3中的用法示例:

{
    ...,"content_security_policy": {
        "extension_pages": "...",
        "sandbox": "..." 
    },
    ...
}

可从Web访问的资源

您需要在manifest.json中进行的最后一个更改是将web_accessible_resources数组更改为详细说明所有资源的对象。 这是一个它在V3中的用法示例:

{
    ...,"web_accessible_resources": {
        "resources": [
            //之前的资源数组
        ]
    },
    ...
}

该对象在未来版本中还将支持键matches(URL数组)、extension_ids(密钥数组)和use_dynamic_url(布尔值)。

添加扩展

现在,如果您转到浏览器中的chrome://extensions并添加或重新加载扩展程序,它将成功更改为清单V3扩展程序。 但是,在我们的示例中,它会在扩展程序框中显示错误按钮,单击该按钮时,它会显示“服务人员注册失败”。 这是因为我们的代码中还有更多工作要做。

从后台脚本到服务人员

首先,什么是服务人员,它与后台脚本有什么区别?

背景脚本在几乎所有扩展中都是必不可少的。 它们允许您执行某些操作或执行代码,而无需用户打开某个页面或执行某些操作。 这可用于发送通知、管理与内容脚本的通信等等。 后台脚本通常始终在后台运行。

服务人员在需要时执行。 与后台脚本不同,它们不会始终在后台运行。 在顶层,服务人员应注册某些事件的侦听器,以允许它们在以后执行。

从后台脚本到服务人员的转变取决于扩展中的代码。 某些扩展可能需要大量重构,而其他扩展则不需要。

您需要执行的第一步是将之前的后台脚本或页面移动到扩展的根目录中。 这实际上是为什么在我们的扩展中收到服务人员注册失败的错误。 我们的后台脚本的路径相对于扩展的根目录是js/assets/background.js

如果您的情况类似,请将后台脚本移动到扩展的根目录中,然后更改清单中的service_worker的值以反映更改:

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

如果重新加载扩展,则应成功注册服务人员。

现在,让我们看看代码。 在我们的扩展中,我们的后台脚本如下所示:

chrome.runtime.onMessage.addListener(function(message, sender, senderResponse){
  if(message.msg === "image"){
    fetch('https://some-random-api.ml/img/pikachu')
          .then(response => response.text())
          .then(data => {
            let dataObj = JSON.parse(data);
            senderResponse({data: dataObj, index: message.index});
          })
          .catch(error => console.log("error", error))
      return true; 
  }
});

基本上,我们的后台脚本使用 chrome.runtime.onMessage.addListener 侦听消息,如果消息请求图像,它会向 API 发送请求,然后将数据返回给我们的内容脚本。

我们的后台脚本实际上不需要任何其他更改。 这是因为现在成为服务人员的后台脚本只注册事件侦听器,并在事件发生时执行代码,这正是服务人员应该做的。

但是,并非所有扩展都是这样的,因为有不同的使用案例。 您需要检查并在后台脚本中修改的内容如下:

全局变量

如上所述,后台脚本以前一直在后台运行。 这意味着如果我有以下代码:

let count = 0;

chrome.runtime.onMessage.addListener( (message) => {
 count++;
 console.log(count);
});

每次后台脚本收到消息时,计数都会递增。 所以,首先是 0,然后是 1,然后是 2,以此类推。

在服务人员中,这将不再起作用。 服务人员只在需要时运行,并在完成工作后终止。 所以,上面的代码将始终在控制台中打印“1”。

更改这取决于您的使用案例。 在上面的示例中,计数可以在后台脚本和内容脚本之间来回传递以获取所需的结果。 一种更好的方法是使用 Chrome 的存储 API。

使用该 API,代码如下所示:

chrome.runtime.onMessage.addListener ( (message) => {
  chrome.storage.local.get(["count"], (result) => {
    const count = result.count ? result.count++ : 1;
    chrome.storage.local.set({count});
    console.log(count);
  });
});

同样,这取决于您的代码,所以请确保根据最适合您的情况进行更改。

计时器和闹铃

计时器在后台脚本中没有问题,因为它们始终在后台运行。 但是,这对于服务人员无效。 您应该使用 闹钟 API 替换所有计时器。

访问 DOM

服务人员无法访问窗口或 DOM。 如果您的扩展需要它,可以使用 jsdom 等库,或者使用 [chrome.windows.create](https://developer.chrome.com/docs /extensions/reference/windows#method-create) 和 chrome.tabs.create。 这取决于您的使用情况以及什么适合您的需求。

如果后台脚本录制音频或视频,这也是必需的,因为这在服务人员中是不可能的。

创建画布

如果您之前的后台脚本创建了 canvas,您仍然可以使用 OffscreenCanvas API 来执行此操作。 您所要做的就是用 OffscreenCanvas 替换 document

例如,如果这是您的代码:

let canvas = document.createElement('canvas'); 

那么你应该改成:

let canvas = new OffscreenCanvas(width, height);

检查您的扩展

在完成将后台脚本更改为服务人员所需的更改后,重新加载浏览器中的扩展程序以查看它是否正常工作。

在我们的示例中,background.js 中除了将其移动到根目录之外不需要任何更改。 因此,如果重新加载扩展并转至某个页面,您会发现图像已成功替换为皮卡丘图像。

操作 API

如前所述,browser_actionpage_action 现在合并为 action。 您的代码中也应该这样。 如果您使用 browserActionpageAction,如下所示:

chrome.browserAction.onClicked.addListener(tab => { ... }); 
chrome.pageAction.onClicked.addListener(tab => { ... });

它应该更改为使用新的操作 API,如下所示:

chrome.action.onClicked.addListener(tab => { ... });

因此,请确保用 action 替换所有 browserActionpageAction 的用法。

executeScript

如果您的代码使用 executeScript 的 code 属性执行任意字符串,则有两种方法可以更改它。 此外,您需要将 chrome.tabs.executeScript 中的 tabs 替换为 scripting,这样它将是 chrome.scripting.executeScript

将代码移动到新文件

您需要将 code 的值移动到一个新文件中,并使用 executeScript 的 file 属性。

例如,如果您的代码如下所示:

chrome.tabs.executeScript({
  code: alert("Hello, World!") 
});

您应该将 code 的值(这里是 alert("Hello, World!"))移动到一个新文件(让我们称之为 hello-world.js):

alert("Hello, World!");

然后将前面的代码更改为:

chrome.scripting.executeScript({
  file: 'hello-world.js'
});

将代码放入函数中

如果您的代码可以放入函数中,如示例代码所示,则只需将其移动到同一文件中的函数中,然后将 executeScriptsfunction 属性分配给您创建的函数:

function greeting() {
  alert("Hello, World!"); 
}

chrome.scripting.executeScript({
  function: greeting  
});

其他工作

您需要在代码中查找的其他更改和内容列表:

  1. 如果您的扩展使用 webRequest API(通常在扩展被强制安装的企业设置中使用),您需要将其替换为 declarativeNetRequest API。

  2. 如果您在内容脚本中进行任何 CORS 请求,请确保将它们移动到您的服务人员中。

  3. 不再允许远程托管的代码。 您需要找到另一种执行远程托管代码的方法。 Chrome 文档建议使用配置驱动的功能和逻辑,这意味着您检索具有所需配置的 JSON 文件并将其本地缓存以供以后使用,或使用远程服务外部化逻辑,这意味着您必须 将应用程序逻辑从扩展移动到远程 Web 服务。

  4. 查看 API 参考,了解您可能正在使用的任何已弃用 API 或方法。