跳转至

contextBridge

<!-- ``YAML history changes: - pr-url: https://github.com/electron/electron/pull/40330 description: "ipcRenderercan no longer be sent over thecontextBridge`" breaking-changes-header: behavior-changed-ipcrenderer-can-no-longer-be-sent-over-the-contextbridge

-->

> 在隔离上下文之间创建一个安全的、双向的、同步的桥接

进程:[渲染进程](../glossary.md#renderer-process)

下面给出了一个从隔离的 preload 脚本向渲染进程暴露 API 的示例:

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

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

```js @ts-nocheck // Renderer (Main World)

window.electron.doThing()

## 术语表 {#glossary}

### 主世界 {#main-world}

“主世界”是主渲染代码运行的 JavaScript 上下文。默认情况下,您在渲染进程中加载的页面会在此世界中执行代码。

### 隔离世界 {#isolated-world}

当在 `webPreferences` 中启用 `contextIsolation` 时(自 Electron 12.0.0 起这是默认行为),您的 `preload` 脚本会在“隔离世界”中运行。有关上下文隔离及其影响的更多信息,请参阅[安全](https://atomgit.com/GitHub_Trending/el/electron/blob/main/docs/tutorial/security.md#3-enable-context-isolation)文档。

## 方法 {#methods}

`contextBridge` 模块具有以下方法:

### `contextBridge.exposeInMainWorld(apiKey, api)` {#contextbridgeexposeinmainworldapikey-api}

* `apiKey` string - 用于将 API 注入到 `window` 的键。API 可通过 `window[apiKey]` 访问。
* `api` any - 您的 API,有关此 API 可以是什么以及其工作原理的更多信息见下文。

### `contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)` {#contextbridgeexposeinisolatedworldworldid-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)` _实验性_ {#contextbridgeexecuteinmainworldexecutionscript-experimental}

<!-- TODO(samuelmaddock): add generics to map the `args` types to the `func` params  -->

* `executionScript` Object
  * `func` (...args: any[]) => any - 要执行的 JavaScript 函数。该函数会被序列化,这意味着任何绑定参数和执行上下文都会丢失。
  * `args` any[](可选) - 要传递给所提供函数的参数数组。这些参数将按照[支持类型表](#parameter--error--return-type-support)在世界之间复制。

返回 `any` - 在主世界中执行函数后返回值的副本。[请参阅表格](#parameter--error--return-type-support)了解值如何在世界之间复制。

## 用法 {#usage}

### API {#api}

提供给 [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api) 的 `api` 必须是 `Function`、`string`、`number`、`Array`、`boolean`,或一个键为字符串、值为 `Function`、`string`、`number`、`Array`、`boolean` 或满足相同条件的嵌套对象。

`Function` 值会被代理到另一个上下文,所有其他值都会被**复制**并**冻结**。通过 API 发送的任何数据/原始值都会变为不可变,桥接任意一侧的更新都不会导致另一侧更新。

下面展示了一个复杂 API 的示例:

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

```js @ts-nocheck // Renderer (In isolated world id1004)

window.electron.doThing()

### API 函数 {#api-functions}

您通过 `contextBridge` 绑定的 `Function` 值会通过 Electron 进行代理,以确保上下文保持隔离。这带来了一些关键限制,如下所述。

#### 参数 / 错误 / 返回类型支持 {#parameter--error--return-type-support}

由于参数、错误和返回值在通过桥接发送时会被**复制**,因此只能使用某些类型。从高层来看,如果您想使用的类型能够被序列化和反序列化为同一个对象,那么它就可以工作。为完整性起见,下面提供了类型支持表:

| 类型 | 复杂度 | 参数支持 | 返回值支持 | 限制 |
| ---- | ---------- | ----------------- | -------------------- | ----------- |
| `string` | 简单 | ✅ | ✅ | 不适用 |
| `number` | 简单 | ✅ | ✅ | 不适用 |
| `boolean` | 简单 | ✅ | ✅ | 不适用 |
| `Object` | 复杂 | ✅ | ✅ | 键必须仅使用本表中的“简单”类型支持。值必须在本表中受支持。原型修改会被丢弃。发送自定义类会复制值,但不会复制原型。 |
| `Array` | 复杂 | ✅ | ✅ | 与 `Object` 类型的限制相同 |
| `Error` | 复杂 | ✅ | ✅ | 抛出的错误也会被复制,这可能导致错误的消息和堆栈跟踪因在不同上下文中抛出而略有变化,并且 Error 对象上的任何自定义属性[都会丢失](https://github.com/electron/electron/issues/25596) |


| 类型 | 复杂度 | 参数支持 | 返回值支持 | 限制 |
| ---- | ---------- | ----------------- | -------------------- | ----------- |
| `Promise` | 复杂 | ✅ | ✅ | 不适用 |
| `Function` | 复杂 | ✅ | ✅ | 原型修改会被丢弃。发送类或构造函数将不起作用。 |
| [可克隆类型](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) | 简单 | ✅ | ✅ | 请参阅链接中关于可克隆类型的文档 |
| `Element` | 复杂 | ✅ | ✅ | 原型修改会被丢弃。发送自定义元素将不起作用。 |
| `Blob` | 复杂 | ✅ | ✅ | 不适用 |
| `VideoFrame` | 复杂 | ✅ | ✅ | 不适用 |
| `Symbol` | 不适用 | ❌ | ❌ | 符号无法跨上下文复制,因此会被丢弃 |

如果您关心的类型不在上表中,则可能不受支持。

### 暴露 ipcRenderer {#exposing-ipcrenderer}

尝试将整个 `ipcRenderer` 模块作为对象通过 `contextBridge` 发送,会导致桥接接收端得到一个空对象。完整发送 `ipcRenderer` 可能允许任何代码发送任何消息,这是一个安全隐患。要通过 `ipcRenderer` 进行交互,请提供如下安全包装器:

```js
// Preload (Isolated World)
contextBridge.exposeInMainWorld('electron', {
  onMyEventName: (callback) => ipcRenderer.on('MyEventName', (e, ...args) => callback(args))
})

```js @ts-nocheck // Renderer (Main World) window.electron.onMyEventName(data => { / ... / })

### 暴露 Node 全局符号 {#exposing-node-global-symbols}

预加载脚本可以使用 `contextBridge` 让您的渲染进程访问 Node API。上述支持类型的表格同样适用于您通过 `contextBridge` 暴露的 Node API。请注意,许多 Node API 会授予访问本地系统资源的权限。对于暴露给不受信任的远程内容的全局变量和 API,请务必谨慎。

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

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 el/electron