跳转到主内容

contextBridge

History

在隔离的上下文中创建一个安全的、双向的、同步的桥梁。

进程: Renderer

如下,是一个从隔离的预加载脚本将 API 暴露给渲染器的示例:

// Preload (Isolated World)
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld(
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing')
}
)
// Renderer (Main World)

window.electron.doThing()

词汇表

Main World

"Main World"是主渲染器代码运行的 JavaScript 上下文。 默认情况下,你在渲染器中加载的页面在此环境中执行代码。

Isolated World

当你在webPreferences属性中启用contextIsolation (Electron 12.0.0 及以上版本默认启用),你的预加载脚本将运行在一个“被隔离的环境”中。 您可以在Security 文档中阅读更多关于上下文隔离及其影响的信息。

方法

contextBridge模块有以下方法:

contextBridge.exposeInMainWorld(apiKey, api)

  • apiKey string - 将 API 注入到 窗口 的键。 API 将可通过 window[apiKey] 访问。
  • api any - 你的 API可以是什么样的以及它是如何工作的相关信息如下。

contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)

  • worldId Integer - 要注入 API 的 world 的 ID。 0 是默认 world,999 的 world 被 Electron 的 contextIsolation 使用。 使用 999 would 为 preload 上下文暴露对象。 我们建议使用 1000+ 来创建隔离的 world。
  • apiKey string - 将 API 注入到 window 的键。 API 将可通过 window[apiKey] 访问。
  • api any - 你的 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 Functions

你通过 contextBridge 绑定的 Function 值会被 Electron 代理,以确保保持上下文隔离。 这导致了我们在下面概述的一些键的限制。

参数 / 错误 / 返回类型支持

因为当参数、错误和返回值在桥接器上发送时会被 复制 ,所以只有某些类型可以使用。 从上层来看,如果你想要使用的类型可以序列化并反序列化为同一个对象,这个类型可以使用。 为确保完整性,下面列出了一份类型支持表:

类型复杂度有入参有返回值局限性
string简单N/A
number简单N/A
布尔值简单N/A
Object复杂键仅支持使用此表中的“简单”的类型。 值必须在此表中支持的值。 修改prototype将被丢弃。 发送自定义类将复制值,但不会包含prototype。
Array复杂Object 类型相同的限制
Error复杂抛出的错误也会被复制,这可能会导致错误的消息和跟踪堆栈由于在不同的上下文中抛出而略有变化,并且 Error 对象的任务自定义属性 将会丢失
Promise复杂N/A
Function复杂修改prototype将被丢弃。 发送类或构造函数将不起作用。
Cloneable Types简单点击链接查看可克隆类型的文档
Element复杂修改prototype将被丢弃。 发送自定义元素将不生效。
Blob复杂N/A
SymbolN/ASymbol不能跨上下文复制,因此会丢弃它们

如果您关心的类型不在上表中,可能是因为不支持该类型。

Exposing ipcRenderer

Attempting to send the entire ipcRenderer module as an object over the contextBridge will result in an empty object on the receiving side of the bridge. Sending over ipcRenderer in full can let any code send any message, which is a security footgun. To interact through ipcRenderer, provide a safe wrapper like below:

// Preload (Isolated World)
contextBridge.exposeInMainWorld('electron', {
onMyEventName: (callback) => ipcRenderer.on('MyEventName', (e, ...args) => callback(args))
})
// Renderer (Main World)
window.electron.onMyEventName(data => { /* ... */ })

暴露Node Global Symbols

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