如何 Extension

最近针对 Edge/Chrome 浏览器开发了几个小工具,这里记录下开发 Extension 的一些要点。

需要关注的文档

Chrome Extension API Reference

该文档非常重要!可以说是开发 Extension 的必备文档,里面包含了所有 API 的详细介绍,以及如何使用这些 API 来开发 Extension,是 Chrome 系插件的重要参考资料。但是 Chrome 的 Get Start 就没必要学了,直接看 Plasmo 的文档足够了。

强烈建议阅读英文原版,中文翻译版可能会有一些过时、遗漏或错误。

Edge Extension

Edge 的 Extension 开发文档,和 Chrome 的 Extension 开发文档非常类似,可以参考 Chrome 的 Extension 开发文档来学习。

Plasmo Docs

Plasmo Github

Plasmo Examples

按照原生的开发方式来开发扩展的话会比较繁琐,这里推荐使用 Plasmo 框架来开发,会省事不少。同时上面列出了 Plasmo 的官方文档和示例代码,可以参考学习。

基本流程

创建项目

pnpm create plasmo

如果需要使用 Plasmo Examples 中的模板,可以使用以下命令:

pnpm create plasmo --with-sidepanel

运行项目

pnpm dev

此时即可在浏览页扩展界面打开开发者模式,选择加载解压缩的代码,从项目的 build 目录中选择 dev 标记的文件夹,即可开始。

打包项目

pnpm build pnpm package

需要注意的是,运行 package 命令前务必先运行 build 命令,否则会使用上次 build 的文件,导致 package 的文件没有更新。

重要概念

Popup 弹出页面

Popup 是 Extension 的一个重要交互页面,通常是用户点击图标后弹出的页面,可以做一些快捷操作。

例如沉浸式翻译的 Popup 页面:

沉浸式翻译的 Popup 页面

该页面在

popup.tsx
文件中定义,开发者可以在这个文件中编写页面的样式和交互逻辑。

同时你也可以放在文件夹下,即

popup/index.tsx
文件中编写页面的逻辑,这样代码结构会更清晰。

需要注意的是 Popup 在每次点击图标后都会重新加载,所以不要在 Popup 中进行过于复杂的操作,否则会影响用户体验。

例如沉浸式翻译的 Popup 页面中的翻译功能,每次点击时其实是跟内容脚本通信,检查每个页面的翻译状态,而不是呆呆的每次都显示翻译按钮。 值得注意的是,与内容脚本通信时,只有当前标签页的内容脚本能收到消息,其它标签页的脚本无法收到消息,或者说只有当前的标签页的脚本才是处于激活状态。

Options 选项页面

Options 页面是 Extension 的设置页面,用户可以在这里进行一些配置。

例如沉浸式翻译的 Options 页面:

沉浸式翻译的 Options 页面

同样,该页面在

options.tsx
文件中定义,开发者可以在这个文件中编写页面的样式和交互逻辑。

同样,你也可以放在文件夹下,即

options/index.tsx
文件中编写页面的逻辑,这样代码结构会更清晰。

Sidepanel 侧边栏

Sidepanel 的样式如下

Plasmo 的 Sidepanel 页面

Sidepanel 对于展示信息和交互上都弥补了其它页面的不足。

Background Service Worker 后台服务工作线程

Background Service Worker 是在整个 Extension 的生命周期内都存在,所以可以用来做一些持久化的任务,可以管理整个扩展的配置、状态或者暴露接口等。

扩展的后台服务工作线程功能强大,因为它运行在服务工作线程环境中。在BSW环境中,无需再担心跨域问题,能够从任意源获取资源(需要注意的是从该环境获取资源的时候,origin 默认还是

chrome-extension://
的形式,因此在严格的API限制下,还是需要进行一些处理,例如 B 站的一些 API 不会响应从该 origin 获取资源,有需要可以参考 declarativeNetRequest)。此外,将复杂的计算任务转移到BSW也是常见的做法。

当然,BSW 也有一些限制,例如不能直接操作 DOM,不能直接访问 UI 等。

通常,BSW 通过消息通信与其它页面进行通信,例如与 Popup 页面通信,或者与 Content Script 通信。也用于管理 Storage 等。

Content Script 内容脚本

Content Script 是注入到页面中的脚本,可以操作页面中的 DOM,可以与页面中的 JavaScript 进行交互,可以与 Background Service Worker 进行通信。

Content Script 通常用于实现一些页面级的功能,例如沉浸式翻译的翻译功能就是通过 Content Script 来实现的。

可以将编写的脚本注入到页面中,类似于 Tampermonkey 和 脚本猫 的脚本。

重要工具

Messages 通信

可以参考使用 Plasmo 的通信文档 来了解如何进行通信。

但通常可以使用 chrome.runtime.sendMessagechrome.runtime.onMessage 来进行通信。

Storage 存储

可以参考使用 Plasmo 的 Storage 文档 来了解如何进行存储。

但通常可以使用 chrome.storage 来进行存储。

通常来说 Chrome 提供了两种 Storage 类型:

  • chrome.storage.local
    :本地存储,不会随着浏览器关闭而消失,会一直存在。
  • chrome.storage.sync
    :同步存储,会随着用户的 Google 账号进行同步,在用户的所有设备上都会有一份相同的数据。

在使用 @plasmohq/storage 时,默认是 sync 的存储类型,如果想要使用 local 的存储类型,需要手动指定。

const storage = new Storage({ area: "local" })

i18n 国际化

参考 Plasmo 的 i18n 文档 来了解如何进行国际化。

务必参考 chrome 的 i18n 文档 来了解如何进行国际化。

基本流程为:

  1. locales
    目录下创建对应语言的 JSON 文件,例如 locales/en/messages.json 和 locales/zh_CN/messages.json。对应的语言代码参考 locales
  2. 在任意位置使用
    chrome.i18n.getMessage('messageName')
    来获取翻译后的文本。

设置默认语言

package.json
中设置
default_locale
字段来设置默认语言。

{ "manifest": { "default_locale": "en" } }

在 package.json 中使用

{ "name": "with-locales-i18n", "displayName": "__MSG_extensionName__", "version": "0.0.0", "description": "__MSG_extensionDescription__", "manifest": { "name": "__MSG_extensionName__" } }

这样你可以为不同的语言设置不同的扩展名及描述。

环境变量

使用 Plasmo 的环境变量文档 来了解如何进行环境变量管理。

Plasmo 中的环境变量请务必都以

PLASMO_PUBLIC_
开头,否则会被忽略。

需要注意的是,当你进行构建时,可以指定某个环境变量为最高优先级,例如:

pnpm build --env=.env.build

在构建时,plasmo 仍然会加载其它环境变量,但会优先使用

.env.build
中的环境变量。