IPC 的必要性
在 Electron 中,渲染进程默认不能直接访问 Node.js 和系统原生能力。如果渲染进程需要读写文件、访问数据库、调用系统 API,必须通过 IPC 让主进程代为执行。
这种设计出于安全考虑——让渲染进程成为一个"沙箱",只运行纯粹的 Web 代码。
IPC 的两种模式
1. 双向请求-响应模式(invoke/handle)
// 主进程注册 handler
const { ipcMain } = require('electron');
ipcMain.handle('db:query', async (event, sql) => {
const db = require('better-sqlite3')('myapp.db');
return db.prepare(sql).all();
});
// 渲染进程发起调用
const results = await window.electronAPI.query('SELECT * FROM users');
2. 单向消息模式(send/on)
// 渲染进程发送消息
ipcRenderer.send('log:info', { message: 'User clicked button' });
// 主进程监听
ipcMain.on('log:info', (event, data) => {
console.log('Log:', data.message);
// 不回复
});
contextBridge:安全 API 暴露
contextBridge 允许你在渲染进程的全局对象上安全地暴露 API:
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
// 文件操作
readFile: (path) => ipcRenderer.invoke('file:read', path),
writeFile: (path, content) =>
ipcRenderer.invoke('file:write', path, content),
// 数据库操作
query: (sql) => ipcRenderer.invoke('db:query', sql),
// 系统信息
getPlatform: () => process.platform,
// 事件订阅
onNotification: (callback) => {
const handler = (event, data) => callback(data);
ipcRenderer.on('notification', handler);
return () => ipcRenderer.removeListener('notification', handler);
}
});
设计良好的 IPC API
原则一:暴露最小权限
只暴露应用真正需要的 API,不要暴露所有 Node.js 能力:
// 错误:暴露了整个 child_process
contextBridge.exposeInMainWorld('api', {
exec: (cmd) => require('child_process').exec(cmd) // 危险!
});
// 正确:只暴露特定功能
contextBridge.exposeInMainWorld('api', {
openExternal: (url) => require('electron').shell.openExternal(url)
});
原则二:验证输入
主进程的 handler 必须验证所有来自渲染进程的输入:
// 错误:直接使用用户输入的路径
ipcMain.handle('file:read', async (event, filePath) => {
return require('fs').readFileSync(filePath); // 危险!
});
// 正确:验证并限制路径
ipcMain.handle('file:read', async (event, filePath) => {
const allowedDir = app.getPath('userData');
const resolved = require('path').resolve(filePath);
if (!resolved.startsWith(allowedDir)) {
throw new Error('Access denied');
}
return require('fs').readFileSync(resolved);
});
IPC 与 Vue/React 集成
Vue 插件方式
// electronApi.js
import { ipcRenderer } from 'electron';
export default {
install(Vue) {
Vue.prototype.$electron = {
readFile: (path) => ipcRenderer.invoke('file:read', path),
writeFile: (path, content) =>
ipcRenderer.invoke('file:write', path, content)
};
}
};
// main.js
Vue.use(electronApi);
// 组件中使用
this.$electron.readFile('/some/path');
React Hook 方式
// useElectron.js
import { useState, useEffect } from 'react';
import { ipcRenderer } from 'electron';
export function useElectron() {
return {
readFile: (path) => ipcRenderer.invoke('file:read', path),
writeFile: (path, content) =>
ipcRenderer.invoke('file:write', path, content)
};
}
// 组件中使用
const { readFile } = useElectron();
这一章想说的
IPC 是 Electron 的核心通信机制:
- invoke/handle:请求-响应模式,适合需要返回值的操作
- send/on:单向消息模式,适合通知类操作
- contextBridge:安全暴露 API 的方式
安全设计原则:最小权限 + 输入验证。