
在 Electron 26+(启用 contextIsolation: true)下,直接向 window 赋值已失效;必须通过 contextBridge.exposeInMainWorld() 安全暴露 API,才能在渲染进程中调用主进程通信能力。
在 electron 26+(启用 `contextisolation: true`)下,直接向 `window` 赋值已失效;必须通过 `contextbridge.exposeinmainworld()` 安全暴露 api,才能在渲染进程中调用主进程通信能力。
Electron 自 v12 起强力推荐并默认启用 contextIsolation: true,而从 v22 开始彻底弃用 nodeIntegration: true 的兼容路径;v26 进一步强化了上下文隔离的安全边界——这意味着你不能再像旧版那样在 preload 脚本中直接写 window.testFunction = ...。该赋值操作将被静默忽略,因为渲染进程的 window 全局对象与 preload 脚本运行的隔离上下文完全分离。
✅ 正确做法:使用 contextBridge.exposeInMainWorld()
这是 Electron 官方唯一支持的安全桥接机制。它将指定的函数、对象或值有选择地、只读地(默认) 注入到渲染进程的 window 对象中,同时阻止原型污染和任意属性访问,确保主进程逻辑不被篡改。
以下是一个生产就绪的 preload.js 示例:
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
// ✅ 安全暴露单个函数
contextBridge.exposeInMainWorld('testFunction', (channel, params) => {
// 参数校验(强烈建议添加)
if (typeof channel !== 'string' || channel.length === 0) {
throw new Error('Invalid IPC channel');
}
ipcRenderer.send(channel, params);
});
// ✅ 可选:暴露带返回值的异步方法(推荐用于响应式交互)
contextBridge.exposeInMainWorld('invokeTest', async (channel, ...args) => {
if (typeof channel !== 'string') throw new Error('Channel must be a string');
return ipcRenderer.invoke(channel, ...args);
});
// ✅ 可选:暴露受控的对象(避免直接暴露 ipcRenderer)
contextBridge.exposeInMainWorld('electronAPI', {
send: (channel, data) => ipcRenderer.send(channel, data),
on: (channel, callback) => {
const validChannels = ['update-available', 'download-progress'];
if (!validChannels.includes(channel)) throw new Error('Forbidden channel');
const wrappedCallback = (event, ...args) => callback(...args);
ipcRenderer.on(channel, wrappedCallback);
return () => ipcRenderer.removeListener(channel, wrappedCallback);
}
});
在渲染进程(如 HTML 或 React/Vue 组件)中即可直接使用:
<!-- index.html -->
<script>
// ✅ 安全可用(无需等待 DOMContentLoaded)
window.testFunction('app:log', { message: 'Hello from renderer!' });
// ✅ 异步调用示例
window.invokeTest('app:getVersion').then(version => {
console.log('Electron version:', version);
});
// ✅ 监听事件
window.electronAPI.on('update-available', (info) => {
alert(`Update ready: ${info.version}`);
});
</script>
⚠️ 重要注意事项:
-
不要在
exposeInMainWorld中暴露原始ipcRenderer实例——这会绕过安全沙箱,导致远程代码执行风险;始终封装为受控函数。 -
始终校验输入参数(尤其是
channel名称),防止恶意渲染进程触发未授权的主进程逻辑。 -
exposeInMainWorld的注入发生在渲染进程 JS 环境初始化早期,早于DOMContentLoaded和window.onload,因此无需监听dom-ready或延迟调用——你的函数在<script></script>标签中可立即使用。 - 若需传递复杂对象,请确保其可被结构化克隆(如
Date、RegExp、Map等需手动序列化)。
总结:contextBridge.exposeInMainWorld() 不是权宜之计,而是 Electron 现代架构下跨上下文通信的基石。拥抱它,意味着更健壮的安全模型、更清晰的接口契约,以及面向未来的应用可维护性。










