人人都会AI编程

19.2 主进程启动优化:延迟加载、按需初始化、精简依赖

更新时间:2026-07-11

主进程的启动速度直接决定了用户从双击图标到看见窗口的时间。一个臃肿的主进程会阻塞 app.whenReady() 的触发,导致窗口迟迟无法创建,用户只能盯着空白屏幕或跳动的小图标发呆。优化主进程启动,本质上就是砍掉一切不是首屏必需的加载和初始化

19.2.1 延迟加载(Lazy Loading)

Node.js 的 require 是同步且会缓存的,在文件顶部一口气引入所有依赖是常见的习惯,但这也意味着即使某个模块在启动阶段根本用不到,它依然会被解析、编译并占用内存。延迟加载的核心就是把那些“未来才可能用到”的模块推迟到真正使用时才引入。

哪些模块适合延迟加载?

  • 自动更新功能(electron-updater):用户安装后不会立刻触发检查更新。
  • 数据库驱动(better-sqlite3nedb):在首次数据读写时才初始化。
  • 日志库、复杂的配置解析、第三方分析服务 SDK。
  • 窗口创建代码:除主窗口外,设置窗口、关于窗口等完全可以在用户点击菜单时才加载对应的构造脚本。

具体做法非常简单:用函数或 getter 将 require 包裹起来。

// ❌ 启动时加载,即使暂时用不到
const { autoUpdater } = require('electron-updater');

// ✅ 按需时再加载
function getAutoUpdater() {
  const { autoUpdater } = require('electron-updater');
  return autoUpdater;
}

更进一步,可以把窗口创建逻辑本身做成懒加载。例如,只在菜单点击事件里 require 对应窗口的模块并创建,而不是在应用启动时就把所有窗口代码全都加载进内存。在大型 Electron 应用中,这种拆分能让启动耗时明显下降。

19.2.2 按需初始化

延迟加载解决的是“代码加载”的问题,而按需初始化更关注“资源分配”和“服务启动”的时机。主进程中有不少资源是启动完成后才逐步使用的,比如托盘图标、全局快捷键、本地服务端口监听、文件监视器等。

一个常见的错误是在 app.whenReady() 中一口气初始化所有服务:

app.whenReady().then(() => {
  createMainWindow();
  setupTray();        // 托盘图标
  registerShortcuts(); // 全局快捷键
  startLocalServer();  // 本地 HTTP 服务
  initFileWatcher();   // 文件监听
});

如果你的应用启动时主窗口才是用户最迫切看到的东西,那么后四行完全可以推迟到主窗口渲染完成之后再执行,或者干脆延迟到用户首次触发相关功能时再初始化。可以借助 setImmediatesetTimeout 或窗口的 'ready-to-show' 事件来实现:

win.once('ready-to-show', () => {
  // 延迟初始化非关键模块
  setTimeout(() => {
    setupTray();
    registerShortcuts();
  }, 200);
});

对于需要前置条件的模块,比如本地服务器只在打开特定功能时才需要,那么就在该功能对应的 IPC 处理函数里按需启动,而不是一上来就建好连接池或端口监听。

19.2.3 精简依赖

主进程中 node_modules 的体量也会影响启动性能。Node.js 在加载模块时需要解析文件路径、读取 package.json 等操作,依赖树越庞大、文件数量越多,启动阶段花费在模块加载上的时间就越长。因此,定期精简依赖是保持主进程轻快的好习惯

实用的精简手段包括:

  • 用小而美的库替代重型库:比如用 dayjs(2KB)替代 moment(数百 KB),用原生 fs.promises 替代 fs-extra(如果不需要递归复制等额外功能)。
  • 移除未使用的依赖:引入 depcheck 工具扫描出那些在 package.json 中声明但实际未 require 的包,果断卸载。
  • 善用 Node.js 内置模块:许多功能实际上 Node.js 自身已经提供,例如 crypto 生成 UUID、URL 解析路径、util.promisify 转换回调风格,不必额外安装第三方包。
  • 检查重复或可合并的依赖:运行 npm lsyarn list 查看依赖树,有时同一个库的多个版本被间接依赖,可以通过 resolutions(Yarn)或 overrides(npm)强制统一版本。
  • 避免在主进程引入前端专用库:像图表库、UI 组件库只属于渲染进程,绝不要在主进程里 require,即使只是偶然被间接引用也会拖慢启动。

此外,利用 Webpack、Vite 等工具的打包能力,将主进程代码与渲染进程代码分别处理,还能对主进程进行 tree-shaking 和代码压缩,进一步减少加载负担。


优化主进程启动是一场“做减法”的工作。每次引入新依赖或添加启动初始化逻辑时,都问自己一句:“这个模块/操作必须在窗口出现之前完成吗?” 通过系统性地延迟加载、按需初始化和精简依赖,你可以让应用像原生程序一样秒出主界面,在用户体验上迈出一大步。