Skip to content

第10章:Electron 调试与错误处理

10.1 渲染进程调试

渲染进程的调试与浏览器调试非常相似,因为渲染进程本质上就是一个 Chromium 浏览器实例。

10.1.1 使用开发者工具

  1. 打开开发者工具

    • 在应用窗口中按下 Ctrl+Shift+I (Windows/Linux) 或 Cmd+Option+I (macOS)
    • 或者在主进程中调用 mainWindow.webContents.openDevTools()
  2. 开发者工具功能

    • Elements:查看和修改 DOM 结构
    • Console:查看控制台输出和执行 JavaScript
    • Sources:调试 JavaScript 代码,设置断点
    • Network:查看网络请求
    • Application:查看本地存储、会话存储等
  3. 设置断点

    • 在 Sources 面板中找到对应的 JavaScript 文件
    • 点击行号设置断点
    • 刷新页面或触发相应事件,程序会在断点处暂停
  4. 调试技巧

    • 使用 console.log() 输出调试信息
    • 使用 debugger 语句在代码中设置断点
    • 使用 Watch 窗口监控变量值
    • 使用 Call Stack 查看函数调用栈

10.2 主进程调试

主进程运行在 Node.js 环境中,调试方式与 Node.js 应用类似。

10.2.1 使用 VS Code 调试

  1. 创建调试配置

    • 在 VS Code 中打开项目
    • 点击左侧的调试图标
    • 点击 "创建 launch.json 文件"
    • 选择 "Electron: Main"
  2. 配置 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"
        }
      ]
    }
  3. 启动调试

    • 点击绿色的 "开始调试" 按钮
    • 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 库进行更高级的日志管理:

  1. 安装依赖

    bash
    npm install electron-log
  2. 使用示例

    javascript
    const 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 通信错误

症状:主进程与渲染进程之间的通信失败

排查方法

  • 检查 ipcMainipcRenderer 是否正确引入
  • 检查消息名称是否一致
  • 检查监听器是否在消息发送前设置
  • 检查是否在渲染进程加载完成后再发送消息

常见原因

  • 模块引入错误(如在渲染进程中使用 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 实现步骤

  1. 修改 main.js

    javascript
    const { 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()
    })
  2. 修改 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>
  3. 安装依赖

    bash
    npm install electron-log

10.5.3 运行效果

  1. 启动应用

    bash
    npm start
  2. 测试功能

    • 点击 "触发渲染进程错误" 按钮,查看错误捕获和显示
    • 点击 "触发 Promise 错误" 按钮,查看 Promise 错误捕获
    • 点击 "触发 IPC 错误" 按钮,查看 IPC 错误处理

10.6 小结

通过本章的学习,你已经掌握了 Electron 应用的调试和错误处理方法:

  • 渲染进程调试:使用开发者工具进行调试,与浏览器调试一致
  • 主进程调试:使用 VS Code 调试配置或日志打印
  • 常见错误排查:了解常见错误类型和排查方法
  • 全局错误捕获:在主进程和渲染进程中设置全局错误捕获
  • 错误处理实践:实现错误信息展示和日志记录

这些技能对于开发稳定、可靠的 Electron 应用至关重要。在实际开发中,应该养成良好的调试习惯,及时捕获和处理错误,提高应用的用户体验。

© 2026 编程马·菜鸟教程 版权所有