调试技巧 #

一、调试概述 #

1.1 调试环境 #

text
┌─────────────────────────────────────────────────────────────┐
│                     Electron 调试                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────┐         ┌─────────────────┐           │
│  │    主进程        │         │   渲染进程       │           │
│  │                 │         │                 │           │
│  │  VS Code 调试   │         │  Chrome DevTools│           │
│  │  Node.js 调试   │         │  控制台日志      │           │
│  │  console.log    │         │  断点调试        │           │
│  └─────────────────┘         └─────────────────┘           │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    IPC 通信调试                       │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 调试工具 #

工具 用途 适用进程
VS Code 调试器 断点、变量查看 主进程
Chrome DevTools DOM、网络、性能 渲染进程
console.log 简单日志 所有进程
electron-log 日志记录 所有进程

二、主进程调试 #

2.1 VS Code 调试配置 #

json
// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Electron: Main Process",
            "type": "node",
            "request": "launch",
            "cwd": "${workspaceFolder}",
            "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
            "args": ["."],
            "outputCapture": "std",
            "env": {
                "NODE_ENV": "development"
            }
        },
        {
            "name": "Electron: Main Process (Attach)",
            "type": "node",
            "request": "attach",
            "port": 5858,
            "address": "localhost"
        }
    ]
}

2.2 命令行调试 #

bash
# 启动调试模式
electron . --inspect=5858

# 等待调试器连接
electron . --inspect-brk=5858

# 然后在 Chrome 中打开
# chrome://inspect

2.3 使用 console.log #

javascript
// main.js
console.log('主进程日志');
console.log('变量值:', someVariable);
console.error('错误信息:', error);
console.time('操作耗时');
// ... 操作
console.timeEnd('操作耗时');

2.4 使用 electron-log #

bash
npm install electron-log
javascript
// main.js
const log = require('electron-log');

log.info('信息日志');
log.warn('警告日志');
log.error('错误日志');

// 设置日志文件
log.transports.file.resolvePath = () => path.join(app.getPath('userData'), 'logs/main.log');

三、渲染进程调试 #

3.1 打开 DevTools #

javascript
// 在主进程中打开
mainWindow.webContents.openDevTools();

// 条件打开
if (isDev) {
    mainWindow.webContents.openDevTools();
}

// 分离窗口
mainWindow.webContents.openDevTools({ mode: 'detach' });

3.2 DevTools 功能 #

javascript
// 控制台
console.log('渲染进程日志');
console.table([{ name: 'John', age: 30 }]);

// 断点
debugger;

// 性能分析
console.time('render');
renderList();
console.timeEnd('render');

// 内存快照
// DevTools -> Memory -> Take heap snapshot

3.3 Vue/React DevTools #

javascript
// 安装 Vue DevTools
// npm install @vue/devtools --save-dev

// 安装 React DevTools
// npm install react-devtools --save-dev

// 在主进程中加载扩展
const { session } = require('electron');
const path = require('path');

app.whenReady().then(() => {
    session.defaultSession.loadExtension(
        path.join(__dirname, 'extensions/vue-devtools')
    );
});

3.4 渲染进程日志 #

javascript
// renderer.js
const originalConsole = { ...console };

console.log = (...args) => {
    originalConsole.log('[Renderer]', ...args);
    // 可选:发送到主进程
    ipcRenderer.send('log', { type: 'log', args });
};

四、IPC 调试 #

4.1 IPC 日志中间件 #

javascript
// main.js
const originalOn = ipcMain.on.bind(ipcMain);
const originalHandle = ipcMain.handle.bind(ipcMain);

ipcMain.on = (channel, listener) => {
    const wrappedListener = (event, ...args) => {
        console.log(`[IPC] 收到: ${channel}`, args);
        listener(event, ...args);
    };
    originalOn(channel, wrappedListener);
};

ipcMain.handle = (channel, listener) => {
    const wrappedListener = async (event, ...args) => {
        console.log(`[IPC] 处理: ${channel}`, args);
        const start = Date.now();
        const result = await listener(event, ...args);
        console.log(`[IPC] 完成: ${channel} (${Date.now() - start}ms)`);
        return result;
    };
    originalHandle(channel, wrappedListener);
};

4.2 渲染进程 IPC 日志 #

javascript
// renderer.js
const originalSend = ipcRenderer.send.bind(ipcRenderer);
const originalInvoke = ipcRenderer.invoke.bind(ipcRenderer);

ipcRenderer.send = (channel, ...args) => {
    console.log(`[IPC] 发送: ${channel}`, args);
    originalSend(channel, ...args);
};

ipcRenderer.invoke = async (channel, ...args) => {
    console.log(`[IPC] 调用: ${channel}`, args);
    const start = Date.now();
    const result = await originalInvoke(channel, ...args);
    console.log(`[IPC] 结果: ${channel} (${Date.now() - start}ms)`);
    return result;
};

五、性能分析 #

5.1 主进程性能 #

javascript
// 使用 Node.js 性能钩子
const { performance, PerformanceObserver } = require('perf_hooks');

const obs = new PerformanceObserver((list) => {
    const entries = list.getEntries();
    entries.forEach((entry) => {
        console.log(`${entry.name}: ${entry.duration}ms`);
    });
});
obs.observe({ entryTypes: ['measure'] });

performance.mark('start');
// ... 操作
performance.mark('end');
performance.measure('操作耗时', 'start', 'end');

5.2 渲染进程性能 #

javascript
// 使用 Performance API
performance.mark('start-render');
renderList();
performance.mark('end-render');
performance.measure('render', 'start-render', 'end-render');

const measures = performance.getEntriesByName('render');
console.log(measures[0].duration);

5.3 内存分析 #

javascript
// 主进程内存
const used = process.memoryUsage();
console.log({
    rss: `${used.rss / 1024 / 1024} MB`,
    heapTotal: `${used.heapTotal / 1024 / 1024} MB`,
    heapUsed: `${used.heapUsed / 1024 / 1024} MB`
});

// 渲染进程内存
// DevTools -> Memory -> Take heap snapshot

5.4 CPU 分析 #

javascript
// 主进程 CPU 分析
const { writeFileSync } = require('fs');

if (isDev) {
    const profiler = require('v8-profiler-next');
    
    profiler.startProfiling('CPU Profile');
    
    setTimeout(() => {
        const profile = profiler.stopProfiling('CPU Profile');
        profile.export((error, result) => {
            writeFileSync('profile.cpuprofile', result);
            profile.delete();
        });
    }, 10000);
}

六、错误处理 #

6.1 全局错误捕获 #

javascript
// 主进程
process.on('uncaughtException', (error) => {
    console.error('未捕获异常:', error);
    // 记录日志
    log.error('Uncaught Exception:', error);
});

process.on('unhandledRejection', (reason, promise) => {
    console.error('未处理的 Promise 拒绝:', reason);
});

// 渲染进程
window.addEventListener('error', (event) => {
    console.error('渲染进程错误:', event.error);
});

window.addEventListener('unhandledrejection', (event) => {
    console.error('未处理的 Promise 拒绝:', event.reason);
});

6.2 错误上报 #

javascript
// 错误上报服务
async function reportError(error, context = {}) {
    try {
        await fetch('https://error-tracking.example.com/api/report', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                error: {
                    message: error.message,
                    stack: error.stack
                },
                context,
                timestamp: new Date().toISOString(),
                platform: process.platform,
                version: app.getVersion()
            })
        });
    } catch (e) {
        console.error('错误上报失败:', e);
    }
}

七、常见问题排查 #

7.1 白屏问题 #

javascript
// 检查加载状态
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
    console.error('页面加载失败:', errorCode, errorDescription);
});

mainWindow.webContents.on('did-finish-load', () => {
    console.log('页面加载完成');
});

// 检查控制台错误
mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
    console.log('控制台消息:', message);
});

7.2 IPC 通信问题 #

javascript
// 检查 IPC 通道
ipcMain.on('debug-channels', () => {
    console.log('已注册的 IPC 处理器:', ipcMain.eventNames());
});

// 检查消息格式
ipcMain.handle('debug-message', (event, data) => {
    console.log('收到消息:', JSON.stringify(data, null, 2));
    return { received: true };
});

7.3 内存泄漏 #

javascript
// 定期检查内存
setInterval(() => {
    const used = process.memoryUsage();
    console.log('内存使用:', used);
    
    // 触发垃圾回收(需要 --expose-gc 标志)
    if (global.gc) {
        global.gc();
    }
}, 60000);

7.4 窗口问题 #

javascript
// 窗口状态调试
mainWindow.on('ready-to-show', () => {
    console.log('窗口准备显示');
});

mainWindow.on('show', () => {
    console.log('窗口显示');
});

mainWindow.on('hide', () => {
    console.log('窗口隐藏');
});

mainWindow.on('close', (event) => {
    console.log('窗口关闭');
});

八、调试工具 #

8.1 Electron Fiddle #

text
Electron Fiddle 是官方提供的快速原型工具
可以快速测试 Electron 代码片段
下载地址: https://www.electronjs.org/fiddle

8.2 开发者工具扩展 #

javascript
// 加载开发者工具扩展
const { session } = require('electron');
const path = require('path');

app.whenReady().then(() => {
    // React DevTools
    session.defaultSession.loadExtension(
        path.join(__dirname, 'extensions/react-devtools')
    );
    
    // Vue DevTools
    session.defaultSession.loadExtension(
        path.join(__dirname, 'extensions/vue-devtools')
    );
});

8.3 日志查看器 #

javascript
// 创建日志查看窗口
function openLogViewer() {
    const logWin = new BrowserWindow({
        width: 800,
        height: 600,
        title: '日志查看器'
    });
    
    logWin.loadFile('log-viewer.html');
}

九、最佳实践 #

9.1 调试检查清单 #

markdown
- [ ] 启用开发者工具
- [ ] 配置 VS Code 调试
- [ ] 添加错误处理
- [ ] 记录关键日志
- [ ] 监控内存使用
- [ ] 检查 IPC 通信
- [ ] 分析性能瓶颈

9.2 生产环境调试 #

javascript
// 生产环境调试开关
const DEBUG_MODE = process.env.ELECTRON_DEBUG === 'true';

if (DEBUG_MODE) {
    mainWindow.webContents.openDevTools();
    // 启用详细日志
}

十、总结 #

10.1 核心要点 #

要点 说明
主进程调试 VS Code 调试器
渲染进程调试 Chrome DevTools
IPC 调试 日志中间件
性能分析 Performance API
错误处理 全局错误捕获

10.2 下一步 #

现在你已经掌握了调试技巧,接下来让我们学习 打包配置,深入了解 Electron 应用的打包和分发!

最后更新:2026-03-28