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
字符串 - 将 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 可以是什么以及它如何工作的更多信息将在下面提供。
用法
API
提供给 exposeInMainWorld
的 api
必须是 Function
、string
、number
、Array
、boolean
或一个键为字符串且值为 Function
、string
、number
、Array
、boolean
的对象,或另一个满足相同条件的嵌套对象。
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 代理,以确保上下文保持隔离。这导致了一些我们在下面概述的关键限制。
参数/错误/返回类型支持
由于参数、错误和返回值在通过桥发送时会被复制,因此只有某些类型可以使用。从高层次上讲,如果您想使用的类型可以序列化和反序列化为同一个对象,它就可以工作。为了完整起见,下面包含一个类型支持表
类型 | 复杂性 | 参数支持 | 返回值支持 | 限制 |
---|---|---|---|---|
字符串 | 简单 | ✅ | ✅ | 不适用 |
数字 | 简单 | ✅ | ✅ | 不适用 |
布尔值 | 简单 | ✅ | ✅ | 不适用 |
对象 | 复杂 | ✅ | ✅ | 键必须只使用此表中的“简单”类型支持。值必须在此表中支持。原型修改将被删除。发送自定义类将复制值,但不会复制原型。 |
数组 | 复杂 | ✅ | ✅ | 与 Object 类型相同的限制 |
错误 | 复杂 | ✅ | ✅ | 抛出的错误也会被复制,这可能会导致由于在不同的上下文中抛出错误,错误的 消息和堆栈跟踪略有变化,并且 Error 对象上的任何自定义属性将会丢失 |
Promise | 复杂 | ✅ | ✅ | 不适用 |
函数 | 复杂 | ✅ | ✅ | 原型修改将被删除。发送类或构造函数将不起作用。 |
可克隆类型 | 简单 | ✅ | ✅ | 请参阅有关可克隆类型的链接文档 |
元素 | 复杂 | ✅ | ✅ | 原型修改将被删除。发送自定义元素将不起作用。 |
Blob | 复杂 | ✅ | ✅ | 不适用 |
Symbol | 不适用 | ❌ | ❌ | 符号无法在上下文之间复制,因此它们会被删除 |
如果您关心的类型不在上表中,则可能不受支持。
公开 ipcRenderer
尝试通过 contextBridge
将整个 ipcRenderer
模块作为对象发送会导致桥接接收端出现一个空对象。完全发送 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')
}
})