跳至主要内容

contextBridge

历史记录

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

进程: 渲染器

下面给出了一个从隔离的预加载脚本向渲染器公开 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 字符串 - 在 window 上注入 API 的键。该 API 可在 window[apiKey] 上访问。
  • api 任何 - 你的 API,有关此 API 可以是什么以及如何工作的更多信息可在下面找到。

contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)

  • worldId 整数 - 要将 API 注入的世界 ID。0 是默认世界,999 是 Electron 的 contextIsolation 功能使用的世界。使用 999 将公开预加载上下文的对象。我们建议在创建隔离世界时使用 1000+。
  • apiKey 字符串 - 在 window 上注入 API 的键。该 API 可在 window[apiKey] 上访问。
  • api 任何 - 你的 API,有关此 API 可以是什么以及如何工作的更多信息可在下面找到。

用法

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 代理,以确保上下文保持隔离。这导致了一些我们下面概述的关键限制。

参数/错误/返回值支持

因为参数、错误和返回值在通过桥梁发送时被复制,所以只有某些类型可以使用。从较高层次来看,如果你要使用的类型可以被序列化和反序列化为同一个对象,它将起作用。为了完整起见,下面列出了类型支持表

类型复杂度参数支持返回值支持限制
字符串简单N/A
数字简单N/A
布尔值简单N/A
对象复杂键必须使用此表中仅“简单”类型支持。值必须在此表中支持。原型修改将被删除。发送自定义类将复制值但不会复制原型。
数组复杂Object 类型相同的限制
错误复杂抛出的错误也会被复制,这会导致错误的消息和堆栈跟踪因在不同的上下文中抛出而略有变化,并且错误对象上的任何自定义属性 将丢失
承诺复杂N/A
函数复杂原型修改将被删除。发送类或构造函数将不起作用。
可克隆类型简单参见有关可克隆类型的链接文档
元素复杂原型修改将被删除。发送自定义元素将不起作用。
Blob复杂N/A
符号N/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 全局符号

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')
}
})