应用生命周期 #

一、应用生命周期概述 #

1.1 生命周期流程图 #

text
┌─────────────────┐
│   应用启动       │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  will-finish-   │
│   launching     │  应用即将完成启动
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│     ready       │  应用准备就绪
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   创建窗口       │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    运行中        │◄──────────────┐
└────────┬────────┘               │
         │                        │
         │  ┌─────────────────┐   │
         └──┤ window-all-closed├──┘
            └─────────────────┘
                    │
                    ▼
            ┌─────────────────┐
            │   before-quit   │  应用即将退出
            └────────┬────────┘
                     │
                     ▼
            ┌─────────────────┐
            │   will-quit     │  应用正在退出
            └────────┬────────┘
                     │
                     ▼
            ┌─────────────────┐
            │      quit       │  应用已退出
            └─────────────────┘

1.2 核心模块 app #

javascript
const { app } = require('electron');

// app 模块提供的主要功能
// - 控制应用生命周期
// - 获取应用信息
// - 管理应用路径
// - 处理系统事件

二、启动阶段 #

2.1 启动事件 #

javascript
const { app, BrowserWindow } = require('electron');

// 应用即将完成启动(macOS 特有)
app.on('will-finish-launching', () => {
    console.log('应用即将完成启动');
    
    // macOS 上处理文件打开
    app.on('open-file', (event, path) => {
        event.preventDefault();
        console.log('打开文件:', path);
    });
});

// 应用准备就绪
app.on('ready', () => {
    console.log('应用准备就绪');
    createWindow();
});

// 推荐使用 whenReady()
app.whenReady().then(() => {
    console.log('应用准备就绪(Promise)');
    createWindow();
});

2.2 创建窗口 #

javascript
let mainWindow = null;

function createWindow() {
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            preload: path.join(__dirname, 'preload.js')
        }
    });

    mainWindow.loadFile('index.html');

    mainWindow.on('closed', () => {
        mainWindow = null;
    });
}

// 确保在应用准备就绪后创建窗口
app.whenReady().then(createWindow);

2.3 macOS 特殊处理 #

javascript
// macOS 上点击 Dock 图标时重新创建窗口
app.on('activate', () => {
    // 在 macOS 上,当点击 Dock 图标且没有其他窗口打开时,
    // 通常会在应用程序中重新创建一个窗口
    if (BrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

三、退出阶段 #

3.1 退出事件 #

javascript
// 所有窗口关闭
app.on('window-all-closed', () => {
    console.log('所有窗口已关闭');
    
    // 在 macOS 上,除非用户按 Cmd + Q,否则应用不会退出
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// 应用即将退出
app.on('before-quit', (event) => {
    console.log('应用即将退出');
    
    // 可以阻止退出
    // event.preventDefault();
});

// 应用正在退出
app.on('will-quit', (event) => {
    console.log('应用正在退出');
    
    // 清理资源
    cleanup();
    
    // 也可以阻止退出
    // event.preventDefault();
});

// 应用已退出
app.on('quit', (event, exitCode) => {
    console.log(`应用已退出,退出码: ${exitCode}`);
});

3.2 退出流程详解 #

javascript
// 完整的退出流程控制
let isQuitting = false;

// 1. 用户触发退出
app.on('before-quit', (event) => {
    if (hasUnsavedChanges()) {
        event.preventDefault();
        
        dialog.showMessageBox({
            type: 'question',
            buttons: ['保存并退出', '放弃更改', '取消'],
            title: '确认退出',
            message: '有未保存的更改'
        }).then((result) => {
            if (result.response === 0) {
                saveChanges().then(() => {
                    isQuitting = true;
                    app.quit();
                });
            } else if (result.response === 1) {
                isQuitting = true;
                app.quit();
            }
        });
    }
});

// 2. 窗口关闭时判断是否退出
mainWindow.on('close', (event) => {
    if (!isQuitting) {
        event.preventDefault();
        mainWindow.hide();
    }
});

// 3. 清理资源
app.on('will-quit', () => {
    // 关闭数据库连接
    // 保存配置
    // 清理临时文件
});

3.3 强制退出 #

javascript
// 正常退出(触发事件)
app.quit();

// 强制退出(不触发事件)
app.exit(0);  // 0 为退出码

// 立即退出(不推荐)
process.exit(0);

四、单例模式 #

4.1 单例锁定 #

javascript
const gotTheLock = app.requestSingleInstanceLock();

if (!gotTheLock) {
    // 如果获取不到锁,说明已有实例运行,退出
    app.quit();
} else {
    // 当第二个实例启动时,聚焦到已有窗口
    app.on('second-instance', (event, commandLine, workingDirectory) => {
        if (mainWindow) {
            if (mainWindow.isMinimized()) {
                mainWindow.restore();
            }
            mainWindow.focus();
        }
    });

    // 创建窗口
    app.whenReady().then(createWindow);
}

4.2 完整单例实现 #

javascript
const { app, BrowserWindow } = require('electron');

let mainWindow = null;

const gotTheLock = app.requestSingleInstanceLock();

if (!gotTheLock) {
    app.quit();
} else {
    app.on('second-instance', (event, commandLine, workingDirectory) => {
        // 处理命令行参数
        const fileToOpen = commandLine.find(arg => !arg.startsWith('-'));
        
        if (mainWindow) {
            if (mainWindow.isMinimized()) {
                mainWindow.restore();
            }
            mainWindow.focus();
            
            // 如果是通过文件关联打开的
            if (fileToOpen) {
                mainWindow.webContents.send('open-file', fileToOpen);
            }
        }
    });

    app.whenReady().then(() => {
        mainWindow = createWindow();
    });

    app.on('window-all-closed', () => {
        if (process.platform !== 'darwin') {
            app.quit();
        }
    });

    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
            mainWindow = createWindow();
        }
    });
}

五、应用信息 #

5.1 基本信息 #

javascript
// 应用名称
console.log('名称:', app.getName());          // 从 package.json 读取
console.log('显示名称:', app.getName());       // 可通过 setName() 设置

// 版本信息
console.log('版本:', app.getVersion());        // 从 package.json 读取
console.log('Electron 版本:', process.versions.electron);
console.log('Chrome 版本:', process.versions.chrome);
console.log('Node 版本:', process.versions.node);
console.log('V8 版本:', process.versions.v8);

// 应用 ID
console.log('应用 ID:', app.getAppPath());

5.2 设置应用信息 #

javascript
// 设置应用名称
app.setName('我的应用');

// 在 package.json 中设置
{
    "name": "my-app",
    "productName": "我的应用",
    "version": "1.0.0"
}

六、应用路径 #

6.1 获取路径 #

javascript
// 应用路径
console.log('应用路径:', app.getAppPath());

// 用户数据目录
console.log('用户数据:', app.getPath('userData'));
// Windows: C:\Users\<user>\AppData\Roaming\<appName>
// macOS: ~/Library/Application Support/<appName>
// Linux: ~/.config/<appName>

// 临时目录
console.log('临时目录:', app.getPath('temp'));

// 桌面目录
console.log('桌面:', app.getPath('desktop'));

// 文档目录
console.log('文档:', app.getPath('documents'));

// 下载目录
console.log('下载:', app.getPath('downloads'));

// 音乐目录
console.log('音乐:', app.getPath('music'));

// 图片目录
console.log('图片:', app.getPath('pictures'));

// 视频目录
console.log('视频:', app.getPath('videos'));

// 日志目录
console.log('日志:', app.getPath('logs'));

// 可执行文件路径
console.log('可执行文件:', app.getPath('exe'));

// 模块路径
console.log('模块:', app.getPath('module'));

6.2 设置路径 #

javascript
// 设置用户数据目录(必须在 ready 之前)
app.setPath('userData', '/custom/user/data/path');

// 设置日志目录
app.setAppLogsPath('/custom/logs/path');

七、系统事件 #

7.1 电源事件 #

javascript
const { powerMonitor } = require('electron');

// 系统挂起
powerMonitor.on('suspend', () => {
    console.log('系统即将挂起');
    // 保存状态
});

// 系统恢复
powerMonitor.on('resume', () => {
    console.log('系统已恢复');
    // 恢复状态
});

// 电源状态变化
powerMonitor.on('on-ac', () => {
    console.log('使用交流电源');
});

powerMonitor.on('on-battery', () => {
    console.log('使用电池');
});

// 锁屏
powerMonitor.on('lock-screen', () => {
    console.log('屏幕已锁定');
});

// 解锁
powerMonitor.on('unlock-screen', () => {
    console.log('屏幕已解锁');
});

// 获取电池状态
const batteryState = powerMonitor.getBatteryState();
console.log('电池状态:', batteryState);

7.2 网络事件 #

javascript
// 网络状态变化(需要手动检测)
let online = navigator.onLine;

setInterval(() => {
    if (navigator.onLine !== online) {
        online = navigator.onLine;
        mainWindow.webContents.send('network-changed', online);
    }
}, 5000);

7.3 系统主题变化 #

javascript
const { nativeTheme } = require('electron');

// 监听主题变化
nativeTheme.on('updated', () => {
    const isDark = nativeTheme.shouldUseDarkColors;
    console.log('系统主题变化:', isDark ? '深色' : '浅色');
    
    // 通知渲染进程
    BrowserWindow.getAllWindows().forEach(win => {
        win.webContents.send('theme-changed', isDark);
    });
});

// 获取当前主题
console.log('深色模式:', nativeTheme.shouldUseDarkColors);
console.log('高对比度:', nativeTheme.shouldUseHighContrastColors);

八、协议注册 #

8.1 自定义协议 #

javascript
// 注册自定义协议
app.setAsDefaultProtocolClient('myapp');

// 处理协议调用
app.on('open-url', (event, url) => {
    event.preventDefault();
    console.log('通过协议打开:', url);
    
    // 解析 URL
    const parsedUrl = new URL(url);
    console.log('协议:', parsedUrl.protocol);
    console.log('主机:', parsedUrl.host);
    console.log('路径:', parsedUrl.pathname);
    console.log('参数:', parsedUrl.searchParams);
});

// macOS 上需要在 Info.plist 中配置
// Windows 上需要在注册表中注册

8.2 深链接处理 #

javascript
// main.js
const { app } = require('electron');

// 注册协议
if (process.defaultApp) {
    if (process.argv.length >= 2) {
        app.setAsDefaultProtocolClient('myapp', process.execPath, [process.argv[1]]);
    }
} else {
    app.setAsDefaultProtocolClient('myapp');
}

// 处理深链接
function handleDeepLink(url) {
    const parsedUrl = new URL(url);
    
    switch (parsedUrl.host) {
        case 'open':
            const fileId = parsedUrl.searchParams.get('id');
            openFile(fileId);
            break;
        case 'settings':
            openSettings();
            break;
    }
}

// Windows/Linux: 从命令行参数获取
if (process.platform !== 'darwin') {
    const deepLink = process.argv.find(arg => arg.startsWith('myapp://'));
    if (deepLink) {
        handleDeepLink(deepLink);
    }
}

// macOS: 从 open-url 事件获取
app.on('open-url', (event, url) => {
    event.preventDefault();
    handleDeepLink(url);
});

九、命令行参数 #

9.1 获取参数 #

javascript
// 获取命令行参数
console.log('参数:', process.argv);

// 示例: electron . --enable-logging --port=3000
// process.argv = ['electron', '.', '--enable-logging', '--port=3000']

// 解析参数
const args = process.argv.slice(2);
const port = args.find(arg => arg.startsWith('--port='))?.split('=')[1] || 3000;

9.2 开发模式检测 #

javascript
// 检测是否为开发模式
const isDev = process.env.NODE_ENV === 'development' || 
              !app.isPackaged;

if (isDev) {
    console.log('开发模式');
    mainWindow.webContents.openDevTools();
} else {
    console.log('生产模式');
}

十、最佳实践 #

10.1 完整启动模板 #

javascript
const { app, BrowserWindow, dialog } = require('electron');
const path = require('path');

let mainWindow = null;
let isQuitting = false;

// 单例模式
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
    app.quit();
}

function createWindow() {
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js'),
            nodeIntegration: false,
            contextIsolation: true
        }
    });

    mainWindow.loadFile('index.html');

    mainWindow.once('ready-to-show', () => {
        mainWindow.show();
    });

    mainWindow.on('close', (event) => {
        if (!isQuitting) {
            event.preventDefault();
            mainWindow.hide();
        }
    });

    mainWindow.on('closed', () => {
        mainWindow = null;
    });

    return mainWindow;
}

// 应用准备就绪
app.whenReady().then(() => {
    createWindow();

    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
            createWindow();
        }
    });
});

// 所有窗口关闭
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// 即将退出
app.on('before-quit', () => {
    isQuitting = true;
});

// 第二个实例
app.on('second-instance', () => {
    if (mainWindow) {
        if (mainWindow.isMinimized()) {
            mainWindow.restore();
        }
        mainWindow.focus();
    }
});

10.2 优雅退出 #

javascript
// 优雅退出处理
async function gracefulShutdown() {
    // 1. 保存数据
    await saveUserData();
    
    // 2. 关闭连接
    await closeConnections();
    
    // 3. 清理临时文件
    cleanupTempFiles();
    
    // 4. 退出
    app.exit(0);
}

// 监听退出信号
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);

// 应用退出前
app.on('before-quit', (event) => {
    event.preventDefault();
    gracefulShutdown();
});

十一、总结 #

11.1 核心要点 #

要点 说明
ready 应用准备就绪,创建窗口
window-all-closed 所有窗口关闭,退出应用
before-quit 应用即将退出,清理资源
单例模式 确保只有一个实例运行
应用路径 获取各种系统路径

11.2 下一步 #

现在你已经掌握了应用生命周期管理,接下来让我们学习 窗口管理,深入了解多窗口管理技术!

最后更新:2026-03-28