跳转到主要内容

contextBridge

历史

创建一个安全、双向、同步的桥梁,跨越隔离的上下文

进程: 渲染器

下面提供了一个示例,展示了如何从隔离的 preload 脚本向渲染器公开 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 string - 用于将 API 注入 window 的键。API 将可以通过 window[apiKey] 访问。
  • api any - 您的 API。有关此 API 可以是什么以及如何工作的更多信息,请参见下文。

contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)

  • worldId Integer - 要将 API 注入的上下文的 ID。0 是默认上下文,999 是 Electron 的 contextIsolation 功能使用的上下文。使用 999 将会公开 preload 上下文的对象。我们建议使用 1000+ 来创建隔离上下文。
  • apiKey string - 用于将 API 注入 window 的键。API 将可以通过 window[apiKey] 访问。
  • api any - 您的 API。有关此 API 可以是什么以及如何工作的更多信息,请参见下文。

contextBridge.executeInMainWorld(executionScript) 实验性

  • executionScript Object
    • func (...args: any[]) => any - 要执行的 JavaScript 函数。此函数将被序列化,这意味着任何绑定的参数和执行上下文都将丢失。
    • args any[] (optional) - 要传递给所提供函数的参数数组。这些参数将在上下文之间按照 支持类型表 进行复制。

返回 any - 执行主上下文中的函数后返回值的副本。有关值如何在上下文之间复制,请 参考表格

用法

API

提供给 exposeInMainWorldapi 必须是 FunctionstringnumberArrayboolean,或者一个键为字符串且值为 FunctionstringnumberArrayboolean 或满足相同条件的另一个嵌套对象的对象。

Function 值将通过代理传递到另一个上下文,而所有其他值都将被 **复制** 并 **冻结**。发送到 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复杂抛出的错误也会被复制,这可能导致错误的消息和堆栈跟踪由于在不同上下文中抛出而略有变化,并且错误对象上的任何自定义属性 都将丢失
Promise复杂N/A
Function复杂原型修改将被丢弃。发送类或构造函数将不起作用。
可克隆类型简单请参阅有关可克隆类型的链接文档
Element复杂原型修改将被丢弃。发送自定义元素将不起作用。
Blob复杂N/A
VideoFrame复杂N/A
SymbolN/A符号无法跨上下文复制,因此它们被丢弃

如果您关心的类型不在上表中,那么它很可能不支持。

公开 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 全局符号

preload 脚本可以使用 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')
}
})