You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Electron + Vite 热重载场景下实现可靠的应用关闭事件处理方案咨询

Electron + Vite 热重载场景下实现可靠的应用关闭事件处理方案咨询

兄弟,你遇到的这个问题我之前做Electron+Vite项目的时候也踩过一模一样的坑!用beforeunload处理关闭逻辑时,用户手动关闭窗口能正常走完异步流程,但Vite热重载时就会卡await engine.save()这步——本质是因为热重载触发时,Electron会快速销毁当前渲染进程,根本不等你的异步操作执行完成就杀进程了。

下面给你几个亲测有效的解决方案,你可以根据项目实际情况选:

方案一:主进程+渲染进程IPC配合,区分场景处理

这个方案的核心是在主进程里监听不同的终止场景(正常关闭/热重载销毁),主动通知渲染进程执行清理逻辑,并且让主进程短暂等待确保逻辑完成。

步骤1:渲染进程封装清理逻辑

把你需要执行的关闭逻辑封装成一个独立函数,最好是能确保在短时间内完成的(如果异步操作耗时太长,热重载体验会受影响):

// 渲染进程代码
export async function performCleanup() {
  engine.isInit = false;
  if (sessionLoaded) {
    await engine.save("", true); // 这里确保save方法是可靠的,能快速完成
  }
  engine.isInit = true;
}

// 监听主进程发来的清理指令
ipcRenderer.on('trigger-cleanup', async () => {
  await performCleanup();
  // 通知主进程清理完成
  ipcRenderer.send('cleanup-done');
});

步骤2:主进程监听不同场景,触发清理

在主进程里,分别处理正常关闭热重载销毁渲染进程的场景:

// 主进程代码
import { app, BrowserWindow, ipcMain } from 'electron';

let mainWindow: BrowserWindow | null = null;

// 正常关闭场景:监听before-quit事件,阻止默认退出,等清理完成再退出
app.on('before-quit', async (event) => {
  if (!mainWindow) return;
  // 阻止默认退出流程
  event.preventDefault();
  // 给渲染进程发清理指令
  mainWindow.webContents.send('trigger-cleanup');
  // 等待清理完成后再退出,加超时兜底避免卡住
  const cleanupDone = new Promise(resolve => {
    const listener = () => {
      ipcMain.removeListener('cleanup-done', listener);
      resolve(true);
    };
    ipcMain.on('cleanup-done', listener);
    setTimeout(resolve, 2000);
  });
  await cleanupDone;
  app.quit();
});

// 热重载场景:监听渲染进程销毁事件,提前触发清理
app.on('renderer-process-destroyed', (event, webContents) => {
  if (webContents === mainWindow?.webContents) {
    // 给即将销毁的渲染进程发清理指令
    webContents.send('trigger-cleanup');
    // 短暂等待异步操作执行完成,别太长否则影响热重载速度
    setTimeout(() => {}, 500);
  }
});

方案二:将清理逻辑移至主进程(如果可行)

如果你的engine.save逻辑不依赖渲染进程的环境,可以直接把这部分逻辑移到主进程里,这样就不需要依赖渲染进程的生命周期,不管是正常关闭还是热重载,主进程都能可靠执行:

// 主进程代码
// 假设engine可以在主进程中实例化
import { engine } from './your-engine-module';

let sessionLoaded = false; // 这里需要和渲染进程同步sessionLoaded状态,比如通过IPC

app.on('before-quit', async () => {
  engine.isInit = false;
  if (sessionLoaded) {
    await engine.save("", true);
  }
  engine.isInit = true;
});

app.on('renderer-process-destroyed', async () => {
  engine.isInit = false;
  if (sessionLoaded) {
    await engine.save("", true);
  }
  engine.isInit = true;
});

这种方式最可靠,因为主进程的生命周期不受Vite热重载的影响,能确保逻辑执行完成。

方案三:用Vite插件拦截热重载流程

如果你想在Vite层面控制热重载,也可以写一个简单的Vite插件,在热重载前通知渲染进程执行清理:

// vite.config.ts
import { defineConfig } from 'vite';
import electron from 'vite-plugin-electron';

export default defineConfig({
  plugins: [
    electron({
      // 你的Electron插件配置
    }),
    // 自定义热重载拦截插件
    {
      name: 'electron-hmr-cleanup',
      configureServer(server) {
        // 监听Vite的热重载事件
        server.hmr?.on('reload', async () => {
          // 给渲染进程发清理指令(需要全局挂载主窗口实例)
          const mainWindow = globalThis.mainWindow;
          if (mainWindow) {
            mainWindow.webContents.send('trigger-cleanup');
            // 短暂等待清理完成
            await new Promise(resolve => setTimeout(resolve, 500));
          }
        });
      }
    }
  ]
});

这种方式能精准拦截Vite的热重载动作,在重载前完成清理。

注意事项

  • 不管用哪种方案,都要确保你的engine.save操作是轻量且快速的,不然会影响热重载的流畅度或者正常关闭的体验
  • 超时兜底很重要,防止某个异常情况导致清理逻辑卡住,影响应用退出或热重载
  • 如果你的清理逻辑必须依赖渲染进程的某些状态,一定要确保状态能正确同步到主进程,或者在渲染进程里可靠执行

试试上面的方案,应该能解决你热重载时清理逻辑执行不全的问题!

火山引擎 最新活动