消息传递API
Plasmo的消息传递API使得扩展不同部分之间的通信变得简单。将文件添加到您的messages
目录中,Plasmo将处理其余的一切。Plasmo消息传递是一种声明式、类型安全、函数式、基于Promise的API,用于在您的扩展组件之间发送、转发和接收消息。
安装
1. 安装依赖
@plasmohq/messaging
库保存在一个单独的仓库中。您首先需要使用包管理器安装它。
pnpm install @plasmohq/messaging
2. 创建background文件夹及文件
@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.ts
3. 生成静态类型
在编译时,Plasmo将为所有消息处理器生成静态类型。如果您运行着开发服务器,这将自动发生;每次构建时也会自动发生。sendToBackground
和relayMessage
函数都在其参数对象中接受一个name
字段;此name
字段将使用所有消息处理器的名称进行静态类型化。
注意:初始类型错误
如果您遇到诸如"name" is never
之类的类型错误,这是因为Plasmo需要编译您的处理器类型。解决方法:
- 运行开发服务器
- 在编辑器中重新启动TypeScript服务器
4. 就是这些
您现在已经成功安装了Plasmo的消息库。
TL;DR
消息传递API | 来源 | 目标 | 单次 | 长期 |
---|---|---|---|---|
消息流 | 扩展页面/CS | BGSW | 是 | 否 |
中继流 | 网站 | CS/BGSW | 是 | 否 |
端口 | 扩展页面/CS | BGSW | 否 | 是 |
端口 | BGSW | 扩展页面/CS | 否 | 是 |
端口+中继 | BGSW | 网页 | 是 | 是 |
示例
消息流
使用消息流在扩展页面、标签页或内容脚本与后台服务工作者之间发起一次性消息。此流对于将繁重的计算卸载到后台服务工作者或绕过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。一旦构建并将其添加到浏览器中,您可以在Chrome的扩展管理器窗口中找到您的扩展ID。
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的扩展管理器中找到这个
})
console.log(resp)
中继流
注意: 中继消息传递API处于公共alpha预览阶段:预计会有bug、不完整/泄漏的抽象以及未来的API更改。请通过此链接报告您遇到的任何问题。
中继流允许目标网页与后台服务工作者之间通过称为中继的轻量级消息处理器进行通信。该中继通过在内容脚本中注册的relayMessage
函数实现。
relayMessage
函数抽象了window.postMessage
机制,注册了一个监听器,检查相同来源的消息并将其转发给后台服务工作者。这些消息随后由注册在background/messages
下的适当消息流处理器处理。
sendToBackgroundViaRelay
函数通过中继发送消息并等待响应。它为每个消息生成唯一的实例ID以确保正确的处理和响应跟踪。
您可以在GitHub仓库 (opens in a new tab)中查看这些函数的实现。
此方法提供了Chrome扩展文档中描述的"externally_connectable" (opens in a new tab)方法的替代方案。
设置中继
要设置中继,请在内容脚本中使用relayMessage
函数。一个内容脚本可以有多个中继。假设前面的例子中有ping
消息处理器,并且网站为www.plasmo.com
:
import type { PlasmoCSConfig } from "plasmo"
import { relayMessage } from "@plasmohq/messaging"
export const config: PlasmoCSConfig = {
matches: ["http://www.plasmo.com/*"] // 只从中继来自此域的消息
}
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("some message was relayed:", req)
return {
message: "Hello from sandbox"
}
}
)
端口
端口消息传递API处于公共alpha预览阶段:预计会有bug、不完整/泄漏的抽象以及未来的API更改。请通过此链接报告您遇到的任何问题。
消息传递端口API是对chrome运行时port API (opens in a new tab)的高级抽象,用于与后台服务工作者建立长期连接。
当前实现的重点在于与后台服务工作者中的端口监听器建立连接:
要创建BGSW端口处理器,请在background/ports
目录中创建一个ts模块。文件名将是端口名称,默认导出将是处理器函数:
import type { PlasmoMessaging } from "@plasmohq/messaging"
const handler: PlasmoMessaging.PortHandler = async (req, res) => {
console.log(req)
res.send({
message: "Hello from port handler"
})
}
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>
以下是usePort
在React中的示例,数据将始终反映端口处理器的最新响应:
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
E2E类型安全(WIP)
端到端请求/响应体类型安全正在进行中,详见#334 (opens in a new tab)。在此期间,您可以使用提供的通用类型:
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: "Hello from background"
})
}
export default handler
import { 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)