编程开发
C/C++
关闭 VS 的符合模式
typedef 与 define
GO
环境安装及修改路径
go 基础
解决go get 下载github项目慢或无法下载的问题
ASCII 码一览表
桌面应用开发 electron
electron ipc通信有啥用
const { app, BrowserWindow, ipcMain, dialog } = require('electron') 的解析
electron 常用模块介绍
Electron 项目解析
前端
python
植入获取当前python脚本所用到的依赖
本文档使用 MrDoc 发布
-
+
首页
Electron 项目解析
## 项目文件 - 一个常规的 Electron 项目所包含的文件: 1. 主进程main.js:创建窗口,处理IPC事件(打开文件对话框、读取文件、执行命令)。 2. preload.js:暴露IPC方法给渲染进程。 3. index.html:界面布局,包含各个元素和按钮。 4. renderer.js:渲染进程中的js。处理DOM事件,调用electronAPI发送IPC消息,处理返回结果。 5. package.json:NodeJS管理项目依赖和配置的文件。 ## 项目分析 - Electron应用分为**主进程**和**渲染进程**,主进程**处理系统相关的操作**,比如文件对话框和执行系统命令,而渲染进程负责**展示界面**。 - 首先,创建主进程main.js,设置基本的窗口,加载index.html。然后创建渲染进程的HTML文件,结构里包含所需的元素。可能需要用preload.js来暴露IPC通信的方法,因为**Electron的新版本默认上下文隔离,不能直接在渲染进程里用require('electron')。注意在preload.js里需要暴露ipcRenderer的方法给渲染进程,比如window.electronAPI**,这样在渲染进程的JS里就可以调用这些方法发送和接收消息。 ### main.js ```js // main.js const { app, BrowserWindow, ipcMain, dialog } = require('electron'); const path = require('path'); const fs = require('fs'); const { exec } = require('child_process'); let mainWindow; // 功能:创建浏览器窗口实例(对应操作系统中的原生窗口) // 必要性:Electron所有GUI操作都基于BrowserWindow对象 // 底层原理:调用Chromium的窗口管理模块 + 系统原生API function createWindow() { mainWindow = new BrowserWindow({ width: 600, height: 500, webPreferences: { // preload指定**隔离脚本** → 唯一安全暴露Node.js能力的方式 // 阻止渲染进程直接访问Node.js模块(默认隔离) // 预加载机制 preload: path.join(__dirname, 'preload.js'), } }); mainWindow.loadFile('index.html'); } // 应用就绪后创建 app.whenReady().then(() => { createWindow(); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); // 处理文件选择 ipcMain.handle('open-file-dialog', async () => { const result = await dialog.showOpenDialog({ properties: ['openFile'] }); if (result.canceled) return { error: '用户取消选择' }; try { const content = fs.readFileSync(result.filePaths[0], 'utf-8'); return { filePath: result.filePaths[0], content: content }; } catch (error) { return { error: error.message }; } }); // 处理命令执行 ipcMain.handle('execute-command', async (_, command) => { return new Promise((resolve) => { exec(command, (error, stdout, stderr) => { if (error) { resolve({ error: error.message }); return; } if (stderr) { resolve({ error: stderr }); return; } resolve({ output: stdout }); }); }); }); // macOS独有行为 app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); ``` ### **预加载机制** ```js preload: path.join(__dirname, 'preload.js') ``` - IPC通信桥梁: ```mermaid graph LR A[渲染进程] -->|IPC| B[预加载脚本 preload.js] B -->|ContextBridge| C[主进程] ``` - **安全优势:** - **白名单式暴露特定API**(而非完全开放Node.js访问) - 阻止恶意代码直接调用系统命令 ### **IPC 处理机制** ```js // 处理文件选择 ipcMain.handle('open-file-dialog', async () => { const result = await dialog.showOpenDialog({ properties: ['openFile'] }); if (result.canceled) return { error: '用户取消选择' }; try { const content = fs.readFileSync(result.filePaths[0], 'utf-8'); return { filePath: result.filePaths[0], content: content }; } catch (error) { return { error: error.message }; } }); ``` 1. **IPC通信注册** ```js ipcMain.handle('open-file-dialog', ...) ``` - **作用机制:** 1. 创建命名通道open-file-dialog 2. 使用handle()而非on()实现Promise异步支持 - **设计考量:** ```mermaid graph TD A[渲染进程] -->|invoke| B[主进程:handle] B -->|返回Promise| A ``` - 保证渲染进程的调用能获得异步响应 - 自动管理事件监听器的生命周期 2. **文件对话框调用** ```js dialog.showOpenDialog({ properties: ['openFile'] }) ``` - **关键参数:** | 属性 | 作用 | 可选值示例 | | :---: | :---: | :---: | | properties| 对话框行为控制| openFile,multiSelections | | filters| 文件类型过滤| { name: 'Text', extensions: ['txt'] } | - **安全限制:** - 默认禁止访问系统特权目录(如~/Library) - 沙箱环境下自动限制文件选择范围 3. **取消操作处理** ```js if (result.canceled) return { error: '...' } ``` - **用户体验设计**: - 明确区分用户主动取消与系统错误 - 避免对取消操作抛出异常 4. **async/await** - async 关键字用于声明一个异步函数,而() =>是箭头函数的定义。所以async () =>组合起来就是一个异步的箭头函数。,这样函数内部就可以使用await关键字来处理异步操作,比如文件读取或API调用。 ```js // 普通箭头函数 () => { /* 函数体 */ } // 异步箭头函数 async () => { await someAsyncTask(); } ``` | 组成部分 | 作用 | | :---: | :---: | | async 关键字 | 声明这是一个异步函数,函数内可以使用 await 关键字 | | () => | 定义箭头函数(无参数时用空括号,单参数可省略括号) | - **Electron 异步操作默认返回 Promise。** 4. **主进程与渲染进程通信** ```mermaid sequenceDiagram Renderer Process->>Main Process: 发送 open-file-dialog 请求 Main Process->>OS: 调用系统对话框 (异步) OS-->>Main Process: 返回用户选择 Main Process->>Renderer Process: 通过 Promise 返回结果 ``` - `ipcMain.handle` 是 Electron 中主进程(Main Process)的一个核心方法,用于处理来自渲染进程(Renderer Process)的异步请求,并返回 Promise 结果。它是 Electron 进程间通信(IPC)的关键工具,通常与渲染进程的 `ipcRenderer.invoke` 方法配合使用。 - 核心作用 - 注册异步请求处理函数 - 在主进程中通过 ipcMain.handle(channel, handler) 注册一个处理函数,当渲染进程调用 ipcRenderer.invoke(channel, ...args) 时,该函数会被触发。 - 自动返回 Promise 结果 - handler 函数可以返回一个 Promise(或直接使用 async 函数),Electron 会自动将结果或错误传递回渲染进程。 ### preload.js ```js // preload.js const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { openFile: () => ipcRenderer.invoke('open-file-dialog'), executeCommand: (command) => ipcRenderer.invoke('execute-command', command) }); ``` ### renderer.js ```js // renderer.js // 选择文件事件绑定 document.addEventListener('DOMContentLoaded', () => { document.getElementById('select-file').addEventListener('click', async () => { const result = await window.electronAPI.openFile(); if (result.error) { alert('错误: ' + result.error); return; } document.getElementById('filePathInput').value = result.filePath; document.getElementById('fileContentTextarea').value = result.content; }); // 执行指令事件绑定 document.getElementById('execute-btn').addEventListener('click', async () => { const command = document.getElementById('commandInput').value; if (!command.trim()) { alert('请输入要执行的命令'); return; } const result = await window.electronAPI.executeCommand(command); if (result.error) { alert(`执行错误: ${result.error}`); } else { alert(`执行结果:\n${result.output}`); } }); }); ``` ### index.html ```js <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Electron File & Command App</title> </head> <body> <h3>文件选择</h3> <input type="text" id="filePathInput" placeholder="文件路径" style="width: 300px;"> <button id="select-file">选择文件</button><br><br> <textarea id="fileContentTextarea" placeholder="文件内容" style="width: 400px; height: 200px;"></textarea> <h3>执行命令</h3> <input type="text" id="commandInput" placeholder="输入系统命令" style="width: 300px;"> <button id="execute-btn">执行</button> <script src="./renderer.js"></script> </body> </html> ``` ### package.json ```json { "name": "nodejstest", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "start": "electron .", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "type": "commonjs" } ```
别卷了
2025年3月31日 16:12
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码