消息传递 API
Plasmo 的消息传递 API 使得扩展不同部分之间的通信变得简单。在您的 messages 目录中添加一个文件,Plasmo 将处理所有其余工作。Plasmo 消息传递是一个声明式、类型安全、基于函数的、基于 Promise 的 API,用于在扩展组件之间发送、转发和接收消息。
安装
1. 安装依赖
@plasmohq/messaging 库保存在一个单独的仓库中。您首先需要使用包管理器安装它。
pnpm install @plasmohq/messaging2. 创建后台文件夹和文件
@plasmohq/messaging 库要求后台服务工作者位于 background/index.ts 文件夹中,所有消息处理程序位于 background/* 文件夹中。
如果您已经有 background.ts 或 background.js 文件,您需要创建一个 background 文件夹并将脚本移动到 background/index.ts 或 background/index.js。
如果您还没有 background 文件夹,请创建一个 background 文件夹并创建一个新的空 background/index.ts 或 background/index.js 文件。
现在您将能够在 background/ 子文件夹中创建新的处理程序。例如,要创建一个名为 ping 的 messages 处理程序,您需要创建 background/messages/ping.ts。请参阅文档的其余部分,了解可用的不同类型的处理程序以及如何配置它们。
此时,您的文件夹结构可能如下所示:
.
├── background
│ ├── index.ts
│ └── messages
│ └── ping.ts3. 生成静态类型
在编译时,Plasmo 将为您的所有消息处理程序生成静态类型。如果您正在运行开发服务器,这将自动发生;每次构建时也会自动发生。sendToBackground 和 relayMessage 函数都接受一个 name 字段作为其参数对象的一部分;这个 name 字段将被静态类型化为您的所有消息处理程序的名称。
注意:初始类型错误
如果您收到诸如 "name" is never 之类的类型错误,这是因为 Plasmo 需要编译您的处理程序类型。要解决此问题:
- 运行开发服务器
- 重新启动编辑器中的 TypeScript 服务器
4. 完成
您现在已经成功安装了 Plasmo 的消息传递库。
快速概览
| 消息传递 API | 从 | 到 | 一次性 | 长连接 |
|---|---|---|---|---|
| 消息流 | 扩展页面/内容脚本 | 后台服务工作者 | 是 | 否 |
| 转发流 | 网页 | 内容脚本/后台服务工作者 | 是 | 否 |
| 端口 | 扩展页面/内容脚本 | 后台服务工作者 | 否 | 是 |
| 端口 | 后台服务工作者 | 扩展页面/内容脚本 | 否 | 是 |
| 端口 + 转发 | 后台服务工作者 | 网页 | 是 | 是 |
示例
消息流
使用消息流在扩展页面、标签页或内容脚本与后台服务工作者之间发起一次性消息。此流程对于将繁重的计算卸载到后台服务工作者或绕过 CORS 非常有用。
后台服务工作者是一个具有 REST 风格 API 处理程序的消息中心。要创建消息处理程序,请在 background/messages 目录中创建一个 ts 模块。文件名应为消息名称,默认导出应为处理程序函数:
import type { PlasmoMessaging } from "@plasmohq/messaging"
const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
const message = await querySomeApi(req.body.id)
res.send({
message
})
}
export default handler扩展页面、内容脚本或标签页可以使用 @plasmohq/messaging 库向这些处理程序发送消息。由于 Plasmo 框架在幕后编排您的处理程序,消息名称是类型化的,并将在您的编辑器中启用 IntelliSense:
import { sendToBackground } from "@plasmohq/messaging"
...
const resp = await sendToBackground({
name: "ping",
body: {
id: 123
}
})
console.log(resp)要从主世界的内容脚本发送消息,您需要在请求中包含扩展的 ID。您的扩展 ID 可以在构建并将其添加到浏览器后,在 Chrome 的扩展管理窗口中找到。
import { sendToBackground } from "@plasmohq/messaging"
import type { PlasmoCSConfig } from "plasmo"
export const config: PlasmoCSConfig = {
matches: ["<all_urls>"],
world: "MAIN"
}
...
const resp = await sendToBackground({
name: "ping",
body: {
id: 123
},
extensionId: 'llljfehhnoeipgngggpomjapaakbkyyy' // 在 Chrome 的扩展管理器中找到此 ID
})
console.log(resp)转发流
注意: 转发消息传递 API 处于公开 alpha 预览阶段:预计会有错误、不完整/有漏洞的抽象以及未来的 API 更改。请通过此链接向我们报告您遇到的任何问题。
转发流支持目标网页和后台服务工作者之间的通信,使用称为转发的轻量级消息处理程序。此转发器在内容脚本中使用 relayMessage 函数注册。
relayMessage 函数抽象了 window.postMessage 机制,注册一个监听器,检查匹配相同来源的消息并将其转发到后台服务工作者。这些消息随后由在 background/messages 下注册的相应消息流处理程序处理。
sendToBackgroundViaRelay 函数通过转发器发送消息并等待响应。它为每条消息生成唯一的实例 ID,以确保正确处理和响应跟踪。
您可以在 GitHub 仓库 中查看这些函数的实现。
此方法提供了 Chrome 扩展文档中描述的 “externally_connectable” 方法的替代方案。
设置转发器
要设置转发器,请在内容脚本中使用 relayMessage 函数。一个内容脚本可以有多个转发器。给定上一个示例中的 ping 消息处理程序和网站 www.plasmocn.org:
import type { PlasmoCSConfig } from "plasmo"
import { relayMessage } from "@plasmohq/messaging"
export const config: PlasmoCSConfig = {
matches: ["http://www.plasmocn.org/*"] // 仅从此域转发消息
}
relayMessage({
name: "ping"
})在目标网页的代码中(例如 plasmo.com),您可以使用 sendToBackgroundViaRelay 通过注册的转发器发送消息,如下所示:
import { sendToBackgroundViaRelay } from "@plasmohq/messaging"
...
const resp = await sendToBackgroundViaRelay({
name: "ping"
})
console.log(resp)要在 chrome.runtime 不可用的上下文中转发消息,您可以使用 relay 函数:
import { relayMessage } from "@plasmohq/messaging"
relayMessage(
{
name: "ping"
},
async (req) => {
console.log("某些消息被转发了:", req)
return {
message: "来自沙盒的问候"
}
}
)端口
端口消息传递 API 处于公开 alpha 预览阶段:预计会有错误、不完整/有漏洞的抽象以及未来的 API 更改。请通过此链接向我们报告您遇到的任何问题。
消息传递端口 API 是 Chrome 运行时 端口 API 的高级抽象,用于与后台服务工作者建立长连接。
当前实现侧重于建立与后台服务工作者中端口监听器的连接:
要创建 BGSW 端口处理程序,请在 background/ports 目录中创建一个 ts 模块。文件名将是端口名称,默认导出将是处理程序函数:
import type { PlasmoMessaging } from "@plasmohq/messaging"
const handler: PlasmoMessaging.PortHandler = async (req, res) => {
console.log(req)
res.send({
message: "来自端口处理程序的问候"
})
}
export default handler在您的扩展页面中,使用 @plasmohq/messaging/port 下的 getPort 实用程序获取端口,或者使用 usePort 钩子,请注意 usePort 目前依赖于 React 钩子,因此您需要在 React 组件中使用它。此示例显示了在 Svelte 组件中使用 getPort:
<script lang="ts">
import { getPort } from "@plasmohq/messaging/port"
import { onMount, onDestroy } from "svelte"
let output = ""
const messageListener = (msg) => {
output = msg
}
const mailPort = getPort("mail")
onMount(() => {
mailPort.onMessage.addListener(messageListener)
})
onDestroy(() => {
mailPort.onMessage.removeListener(messageListener)
})
function handleSubmit() {
mailPort.postMessage({
body: {
hello: "world"
}
})
}
</script>
<div>{output}</div>以下是 React 中使用 usePort 的示例,数据将始终反映来自端口处理程序的最新响应:
import { usePort } from "@plasmohq/messaging/hook"
function DeltaTab() {
const mailPort = usePort("mail")
return (
<div>
{mailPort.data?.message}
<button
onClick={async () => {
mailPort.send({
hello: "world"
})
}}>
发送数据
</button>
</div>
)
}
export default DeltaTab端到端类型安全(进行中)
端到端请求/响应主体类型安全正在 #334 进行中。在此期间,您可以使用提供的泛型类型:
import type { PlasmoMessaging } from "@plasmohq/messaging"
export type RequestBody = {
id: number
}
export type ResponseBody = {
message: string
}
const handler: PlasmoMessaging.MessageHandler<
RequestBody,
ResponseBody
> = async (req, res) => {
console.log(req.body.id)
res.send({
message: "来自后台的问候"
})
}
export default handlerimport { sendToBackground } from "@plasmohq/messaging"
import type { RequestBody, ResponseBody } from "~background/messages/ping"
...
const resp = await sendToBackground<RequestBody, ResponseBody>({
name: "ping",
body: {
id: 123
}
})
console.log(resp)