contextBridge
历史
| 版本(s) | 更改 |
|---|---|
None |
|
创建安全、双向、同步的跨隔离上下文桥梁
进程: 渲染进程
以下是一个将 API 暴露给渲染器,从隔离的预加载脚本中的示例
// Preload (Isolated World)
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld(
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing')
}
)
// Renderer (Main World)
window.electron.doThing()
术语表
主世界
“主世界”是你的主渲染器代码运行的 JavaScript 上下文。默认情况下,你在渲染器中加载的页面会在这个世界中执行代码。
隔离世界
当你的 webPreferences 中启用 contextIsolation 时(自 Electron 12.0.0 起,这是默认行为),你的 preload 脚本会在“隔离世界”中运行。你可以在 安全 文档中了解更多关于上下文隔离及其影响的信息。
方法
contextBridge 模块具有以下方法
contextBridge.exposeInMainWorld(apiKey, api)
apiKey字符串 - 将 API 注入到window中的键。API 将在window[apiKey]上可用。api任意 - 你的 API,关于此 API 可以是什么以及如何工作的更多信息在下面提供。
contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)
worldId整数 - 将 API 注入到的世界的 ID。0是默认世界,999是 Electron 的contextIsolation功能使用的世界。使用 999 会将对象暴露给预加载上下文。我们建议在创建隔离世界时使用 1000+。apiKey字符串 - 将 API 注入到window中的键。API 将在window[apiKey]上可用。api任意 - 你的 API,关于此 API 可以是什么以及如何工作的更多信息在下面提供。
contextBridge.executeInMainWorld(executionScript) 实验性
executionScript对象func(...args: any[]) => any - 要执行的 JavaScript 函数。此函数将被序列化,这意味着任何绑定的参数和执行上下文都将丢失。args任意[] (可选) - 要传递给提供的函数的参数数组。这些参数将根据 支持的类型表 在世界之间复制。
返回 any - 执行函数在主世界中产生的结果值的副本。 请参阅该表,了解值如何在世界之间复制。
用法
API
提供给 exposeInMainWorld 的 api 必须是 Function、string、number、Array、boolean,或者其键为字符串且值为 Function、string、number、Array、boolean 或满足相同条件的另一个嵌套对象的对象。
Function 值通过 Electron 代理到另一个上下文,而所有其他值都将被 复制 和 冻结。通过 API 发送的任何数据/原始值都将变为不可变,并且桥梁两侧的更新不会导致另一侧的更新。
下面显示了一个复杂的 API 示例
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld(
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing'),
myPromises: [Promise.resolve(), Promise.reject(new Error('whoops'))],
anAsyncFunction: async () => 123,
data: {
myFlags: ['a', 'b', 'c'],
bootTime: 1234
},
nestedAPI: {
evenDeeper: {
youCanDoThisAsMuchAsYouWant: {
fn: () => ({
returnData: 123
})
}
}
}
}
)
下面显示了一个 exposeInIsolatedWorld 示例
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInIsolatedWorld(
1004,
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing')
}
)
// Renderer (In isolated world id1004)
window.electron.doThing()
API 函数
通过 contextBridge 绑定的 Function 值通过 Electron 代理,以确保上下文保持隔离。这导致了一些关键的限制,我们将在下面概述。
参数 / 错误 / 返回类型支持
由于参数、错误和返回值在通过桥梁发送时被 复制,因此只有某些类型可以使用。从高层来看,如果你想使用的类型可以序列化和反序列化为相同对象,那么它就可以工作。为了完整起见,下面包含了一个类型支持表
| 类型 | 复杂度 | 参数支持 | 返回值支持 | 限制 |
|---|---|---|---|---|
string | 简单 | ✅ | ✅ | N/A |
number | 简单 | ✅ | ✅ | N/A |
boolean | 简单 | ✅ | ✅ | N/A |
Object | 复杂 | ✅ | ✅ | 键必须仅使用此表中的“简单”类型支持。值必须在此表中支持。原型修改将被丢弃。发送自定义类将复制值,但不会复制原型。 |
Array | 复杂 | ✅ | ✅ | 与 Object 类型相同的限制 |
Error | 复杂 | ✅ | ✅ | 抛出的错误也会被复制,这可能会导致错误的消息和堆栈跟踪略有变化,因为它们是在不同的上下文中抛出的,并且 Error 对象上的任何自定义属性 将会丢失 |
Promise | 复杂 | ✅ | ✅ | N/A |
Function | 复杂 | ✅ | ✅ | 原型修改将被丢弃。发送类或构造函数将不起作用。 |
| 可克隆类型 | 简单 | ✅ | ✅ | 请参阅有关可克隆类型的链接文档 |
Element | 复杂 | ✅ | ✅ | 原型修改将被丢弃。发送自定义元素将不起作用。 |
Blob | 复杂 | ✅ | ✅ | N/A |
VideoFrame | 复杂 | ✅ | ✅ | N/A |
Symbol | N/A | ❌ | ❌ | Symbols 无法在上下文之间复制,因此它们将被丢弃 |
如果你的关心的类型不在上表中,它可能不受支持。
暴露 ipcRenderer
尝试将整个 ipcRenderer 模块作为对象通过 contextBridge 发送,将在桥梁的接收端得到一个空对象。完全发送 ipcRenderer 可能会让任何代码发送任何消息,这是一个安全隐患。要通过 ipcRenderer 交互,请提供如下安全包装器
// Preload (Isolated World)
contextBridge.exposeInMainWorld('electron', {
onMyEventName: (callback) => ipcRenderer.on('MyEventName', (e, ...args) => callback(args))
})
// Renderer (Main World)
window.electron.onMyEventName(data => { /* ... */ })
暴露 Node 全局符号
预加载脚本可以使用 contextBridge 将你的渲染器访问 Node API。上面描述的支持类型表也适用于你通过 contextBridge 暴露的 Node API。请注意,许多 Node API 授予对本地系统资源的访问权限。请非常小心你向不受信任的远程内容暴露哪些全局变量和 API。
const { contextBridge } = require('electron')
const crypto = require('node:crypto')
contextBridge.exposeInMainWorld('nodeCrypto', {
sha256sum (data) {
const hash = crypto.createHash('sha256')
hash.update(data)
return hash.digest('hex')
}
})