Appearance
第10章:Electron 调试与错误处理
10.1 渲染进程调试
渲染进程的调试与浏览器调试非常相似,因为渲染进程本质上就是一个 Chromium 浏览器实例。
10.1.1 使用开发者工具
打开开发者工具
- 在应用窗口中按下
Ctrl+Shift+I(Windows/Linux) 或Cmd+Option+I(macOS) - 或者在主进程中调用
mainWindow.webContents.openDevTools()
- 在应用窗口中按下
开发者工具功能
- Elements:查看和修改 DOM 结构
- Console:查看控制台输出和执行 JavaScript
- Sources:调试 JavaScript 代码,设置断点
- Network:查看网络请求
- Application:查看本地存储、会话存储等
设置断点
- 在 Sources 面板中找到对应的 JavaScript 文件
- 点击行号设置断点
- 刷新页面或触发相应事件,程序会在断点处暂停
调试技巧
- 使用
console.log()输出调试信息 - 使用
debugger语句在代码中设置断点 - 使用 Watch 窗口监控变量值
- 使用 Call Stack 查看函数调用栈
- 使用
10.2 主进程调试
主进程运行在 Node.js 环境中,调试方式与 Node.js 应用类似。
10.2.1 使用 VS Code 调试
创建调试配置
- 在 VS Code 中打开项目
- 点击左侧的调试图标
- 点击 "创建 launch.json 文件"
- 选择 "Electron: Main"
配置 launch.json
json{ "version": "0.2.0", "configurations": [ { "name": "Electron: Main", "type": "node", "request": "launch", "cwd": "${workspaceFolder}", "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron", "windows": { "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd" }, "args": ["."], "outputCapture": "std" } ] }启动调试
- 点击绿色的 "开始调试" 按钮
- VS Code 会启动 Electron 应用并进入调试模式
- 在主进程代码中设置断点进行调试
10.2.2 使用日志打印
对于简单的调试,使用 console.log() 是最直接的方法:
javascript
// 主进程中
console.log('主进程启动')
// 监听应用就绪事件
app.whenReady().then(() => {
console.log('应用就绪')
createWindow()
})
// 窗口创建
function createWindow() {
console.log('创建窗口')
// ...
}10.2.3 使用 electron-log
对于更复杂的应用,可以使用 electron-log 库进行更高级的日志管理:
安装依赖
bashnpm install electron-log使用示例
javascriptconst log = require('electron-log') // 配置日志 log.transports.file.level = 'info' log.transports.console.level = 'debug' // 使用日志 log.info('应用启动') log.warn('警告信息') log.error('错误信息') log.debug('调试信息')
10.3 常见错误类型与排查方法
10.3.1 启动失败
症状:应用无法启动,可能出现白屏或崩溃
排查方法:
- 检查主进程文件是否存在语法错误
- 检查依赖是否安装完整(运行
npm install) - 检查
package.json中的main字段是否正确 - 检查端口是否被占用
- 查看控制台输出的错误信息
常见原因:
- 主进程文件路径错误
- 依赖缺失或版本不兼容
- 语法错误(如缺少分号、括号不匹配)
- 端口冲突
10.3.2 IPC 通信错误
症状:主进程与渲染进程之间的通信失败
排查方法:
- 检查
ipcMain和ipcRenderer是否正确引入 - 检查消息名称是否一致
- 检查监听器是否在消息发送前设置
- 检查是否在渲染进程加载完成后再发送消息
常见原因:
- 模块引入错误(如在渲染进程中使用
ipcMain) - 消息名称不匹配
- 监听器设置时机错误
- 渲染进程还未加载完成就发送消息
10.3.3 原生 API 调用失败
症状:调用 Electron 原生 API 时出错
排查方法:
- 检查模块是否正确引入
- 检查 API 调用的参数是否正确
- 检查是否在正确的进程中调用 API
- 查看控制台输出的错误信息
常见原因:
- 模块未引入或引入错误
- 参数类型或格式错误
- 在错误的进程中调用 API(如在渲染进程中直接使用
app模块) - 权限不足
10.4 全局错误捕获
为了提高应用的稳定性,应该在主进程和渲染进程中都设置全局错误捕获。
10.4.1 主进程错误捕获
javascript
// 主进程错误捕获
process.on('uncaughtException', (error) => {
console.error('主进程未捕获异常:', error)
// 可以在这里添加错误处理逻辑,如记录日志、显示错误对话框等
})
// 未处理的 Promise 拒绝捕获
process.on('unhandledRejection', (reason, promise) => {
console.error('主进程未处理的 Promise 拒绝:', reason)
// 处理逻辑
})10.4.2 渲染进程错误捕获
javascript
// 渲染进程错误捕获
window.addEventListener('error', (event) => {
console.error('渲染进程错误:', event.error)
// 处理逻辑
event.preventDefault() // 阻止默认处理
})
// 未处理的 Promise 拒绝捕获
window.addEventListener('unhandledrejection', (event) => {
console.error('渲染进程未处理的 Promise 拒绝:', event.reason)
// 处理逻辑
event.preventDefault() // 阻止默认处理
})10.5 实操案例:调试应用错误
10.5.1 场景描述
创建一个 Electron 应用,实现以下功能:
- 全局错误捕获
- 错误信息展示
- 错误日志记录
10.5.2 实现步骤
修改 main.js
javascriptconst { app, BrowserWindow, dialog } = require('electron') const path = require('path') const log = require('electron-log') // 配置日志 log.transports.file.level = 'info' log.transports.console.level = 'debug' // 全局错误捕获 process.on('uncaughtException', (error) => { log.error('主进程未捕获异常:', error) showErrorDialog('应用错误', `发生未预期的错误:\n${error.message}`) }) process.on('unhandledRejection', (reason) => { log.error('主进程未处理的 Promise 拒绝:', reason) showErrorDialog('应用错误', `发生未预期的错误:\n${reason.message || reason}`) }) let mainWindow function createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, contextIsolation: false } }) mainWindow.loadFile('index.html') mainWindow.webContents.openDevTools() mainWindow.on('closed', () => { mainWindow = null }) } function showErrorDialog(title, message) { dialog.showMessageBox({ type: 'error', title: title, message: message, buttons: ['确定'] }) } app.whenReady().then(createWindow) app.on('window-all-closed', function () { if (process.platform !== 'darwin') app.quit() }) app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow() })修改 index.html
html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>错误处理示例</title> <style> body { font-family: Arial, sans-serif; margin: 20px; padding: 0; background-color: #f0f0f0; } .container { max-width: 700px; margin: 0 auto; background-color: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } h1 { color: #333; text-align: center; } .button-container { display: flex; justify-content: center; gap: 10px; margin: 20px 0; } button { padding: 10px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background-color: #45a049; } .error-section { margin-top: 20px; padding: 15px; background-color: #ffebee; border: 1px solid #ffcdd2; border-radius: 4px; display: none; } .error-section.show { display: block; } #error-message { color: #c62828; } </style> </head> <body> <div class="container"> <h1>错误处理示例</h1> <div class="button-container"> <button id="trigger-error">触发渲染进程错误</button> <button id="trigger-promise-error">触发 Promise 错误</button> <button id="trigger-ipc-error">触发 IPC 错误</button> </div> <div class="error-section" id="error-section"> <h3>错误信息</h3> <div id="error-message"></div> </div> </div> <script> const { ipcRenderer } = require('electron') // 全局错误捕获 window.addEventListener('error', (event) => { console.error('渲染进程错误:', event.error) showError(event.error.message) event.preventDefault() }) window.addEventListener('unhandledrejection', (event) => { console.error('渲染进程未处理的 Promise 拒绝:', event.reason) showError(event.reason.message || event.reason) event.preventDefault() }) // 显示错误信息 function showError(message) { const errorSection = document.getElementById('error-section') const errorMessage = document.getElementById('error-message') errorMessage.textContent = message errorSection.classList.add('show') } // 触发渲染进程错误 document.getElementById('trigger-error').addEventListener('click', () => { // 故意触发错误 undefinedFunction() }) // 触发 Promise 错误 document.getElementById('trigger-promise-error').addEventListener('click', () => { // 故意触发 Promise 错误 new Promise((resolve, reject) => { reject(new Error('Promise 错误')) }) }) // 触发 IPC 错误 document.getElementById('trigger-ipc-error').addEventListener('click', () => { // 发送一个主进程未监听的消息 ipcRenderer.send('non-existent-channel', 'test') }) </script> </body> </html>安装依赖
bashnpm install electron-log
10.5.3 运行效果
启动应用
bashnpm start测试功能
- 点击 "触发渲染进程错误" 按钮,查看错误捕获和显示
- 点击 "触发 Promise 错误" 按钮,查看 Promise 错误捕获
- 点击 "触发 IPC 错误" 按钮,查看 IPC 错误处理
10.6 小结
通过本章的学习,你已经掌握了 Electron 应用的调试和错误处理方法:
- 渲染进程调试:使用开发者工具进行调试,与浏览器调试一致
- 主进程调试:使用 VS Code 调试配置或日志打印
- 常见错误排查:了解常见错误类型和排查方法
- 全局错误捕获:在主进程和渲染进程中设置全局错误捕获
- 错误处理实践:实现错误信息展示和日志记录
这些技能对于开发稳定、可靠的 Electron 应用至关重要。在实际开发中,应该养成良好的调试习惯,及时捕获和处理错误,提高应用的用户体验。
