如何在关闭Electron应用时原生显示保存更改提示对话框?
实现Electron原生“未保存更改”提示方案
嘿,这个需求我刚好实操过,完全可以用Electron的原生API实现,不用自定义对话框!我给你一步步拆解怎么做,完美适配macOS的原生提示(比如关闭按钮变红点)和跨平台的关闭前确认:
核心思路
我们需要做三件事:
- 跟踪应用的“未保存更改”状态
- 在窗口关闭/应用退出时触发原生确认对话框
- 针对macOS系统,启用原生的未保存状态视觉提示(关闭按钮红点、标题星号)
1. 跟踪未保存状态
首先,在主进程里维护一个状态变量(比如hasUnsavedChanges),当用户修改内容时(比如在渲染进程输入、操作),通过IPC通知主进程更新这个变量。
渲染进程示例(通知状态变化)
// 假设用户在编辑区域输入时触发 document.querySelector('#editor').addEventListener('input', () => { // 通知主进程:现在有未保存更改 window.ipcRenderer.send('update-unsaved-status', true); }); // 用户点击保存按钮后,通知主进程状态重置 document.querySelector('#save-btn').addEventListener('click', async () => { // 执行你的保存逻辑(比如写入文件) await saveToFile(); // 通知主进程:已保存,无未更改内容 window.ipcRenderer.send('update-unsaved-status', false); });
2. 主进程处理关闭事件
在主进程中,我们需要监听两个关键事件:窗口的close事件(用户点击关闭按钮)和应用的before-quit事件(用户直接退出应用,比如macOS菜单栏的“退出”),在这两个事件里检查未保存状态,触发原生对话框。
主进程核心代码
const { app, BrowserWindow, dialog } = require('electron'); const path = require('path'); let mainWindow; let hasUnsavedChanges = false; function createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { contextIsolation: true, preload: path.join(__dirname, 'preload.js') // 用于IPC通信的预加载脚本 } }); // 监听窗口关闭事件 mainWindow.on('close', (event) => { handleUnsavedChanges(event); }); // 监听应用退出事件(比如用户点击菜单栏的退出) app.on('before-quit', (event) => { if (hasUnsavedChanges) { event.preventDefault(); // 触发窗口的close事件,复用同一个处理逻辑 mainWindow.emit('close'); } }); // 监听渲染进程发来的状态更新 mainWindow.webContents.on('ipc-message', (_, channel, args) => { if (channel === 'update-unsaved-status') { hasUnsavedChanges = args; // 针对macOS设置原生未保存提示 if (process.platform === 'darwin') { mainWindow.setDocumentEdited(args); // 开启/关闭关闭按钮红点 } // 跨平台:窗口标题加星号标识未保存 const baseTitle = '我的Electron应用'; mainWindow.setTitle(args ? `${baseTitle}*` : baseTitle); } }); } // 处理未保存更改的核心逻辑 function handleUnsavedChanges(event) { if (!hasUnsavedChanges) return; // 阻止默认关闭行为 event.preventDefault(); // 显示原生系统对话框 dialog.showMessageBox(mainWindow, { type: 'question', buttons: ['保存', '不保存', '取消'], defaultId: 0, // 默认选中“保存” title: '未保存的更改', message: '你有未保存的更改,是否要保存后再退出?' }).then((result) => { switch (result.response) { case 0: // 用户选择“保存” saveChanges().then(() => { hasUnsavedChanges = false; mainWindow.close(); // 保存完成后关闭窗口 }); break; case 1: // 用户选择“不保存” hasUnsavedChanges = false; mainWindow.close(); // 直接关闭,丢弃更改 break; case 2: // 用户选择“取消” // 什么都不做,窗口保持打开 break; } }); } // 示例保存函数(替换成你的实际保存逻辑) function saveChanges() { return new Promise((resolve) => { // 这里写你的文件写入逻辑 console.log('正在保存更改到文件...'); setTimeout(resolve, 600); // 模拟异步保存过程 }); } // 常规Electron应用生命周期代码 app.whenReady().then(createWindow); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); });
3. 关键原生特性说明
- macOS专属:
setDocumentEdited(true)
调用这个API后,macOS系统会自动把窗口的红色关闭按钮变成带小点的样式,这就是你要的原生未保存提示效果,完全符合系统交互规范。 - 跨平台标题星号
在窗口标题后加*是跨平台通用的未保存标识,Windows和Linux用户也能快速识别。 - 原生对话框
dialog.showMessageBox()调用的是系统原生的对话框组件,风格和系统完全一致,比自定义对话框更符合用户习惯。
内容的提问来源于stack exchange,提问作者David Cruz
相关产品推荐
相关产品推荐

