Electron:阻止窗口关闭但允许页面重定向的实现方案
问题场景
我有一个简单的Electron应用,主进程会打开一个名为mainWindow的浏览器窗口。出于kiosk场景需求(已设置kiosk: true,需额外添加一层保护),我希望阻止用户关闭该窗口。我在渲染进程中设置了beforeunload事件监听器,代码如下:
window.onbeforeunload = (e) => { console.log('I do not want to be closed') e.returnValue = false }
该设置能有效阻止窗口关闭(至少在macOS系统中),且可通过主进程的mainWindow.destroy()退出进程。但问题在于,此设置会导致无法通过location.href或mainWindow.loadFile()实现页面重定向,因为重定向会触发beforeunload事件并被拦截。除了使用全屏iframe的方案外,是否有办法区分beforeunload事件是因窗口关闭触发还是页面重定向触发?
解决方案
方案一:主进程层面拦截窗口关闭(最简洁可靠)
既然需求是阻止用户关闭窗口,完全可以把逻辑移到主进程,监听窗口的close事件并阻止默认行为,这样渲染进程不需要设置beforeunload,重定向操作不受任何影响。
主进程代码示例:
mainWindow.on('close', (e) => { // 拦截用户触发的窗口关闭操作 e.preventDefault(); // 若需要主动退出应用,直接调用mainWindow.destroy()即可,该方法不会触发close事件 });
这种方式直接从根源解决问题:用户点击关闭按钮的操作会被拦截,而mainWindow.loadFile()或渲染进程的location.href重定向完全不受限制,因为没有渲染层的beforeunload拦截。
方案二:渲染进程添加标记变量区分主动重定向
如果必须在渲染进程处理beforeunload,可以通过一个全局标记变量,区分是主动发起的重定向还是用户触发的窗口关闭。
渲染进程主动重定向的情况:
let isRedirecting = false; // 封装主动重定向的函数 function redirectTo(url) { isRedirecting = true; location.href = url; // 延迟重置标记,避免后续误判 setTimeout(() => { isRedirecting = false; }, 100); } window.onbeforeunload = (e) => { if (isRedirecting) { return; // 允许重定向,不拦截 } console.log('I do not want to be closed'); e.returnValue = false; };
主进程调用loadFile的情况:
通过IPC通知渲染进程设置标记:
主进程代码:
// 自定义加载页面的函数 function loadNewPage(filePath) { mainWindow.webContents.send('will-redirect'); mainWindow.loadFile(filePath); }
渲染进程代码:
const { ipcRenderer } = require('electron'); let isRedirecting = false; ipcRenderer.on('will-redirect', () => { isRedirecting = true; setTimeout(() => { isRedirecting = false; }, 100); }); window.onbeforeunload = (e) => { if (isRedirecting) { return; } console.log('I do not want to be closed'); e.returnValue = false; };
方案三:主进程监听webContents的will-prevent-unload事件
利用Electron提供的will-prevent-unload事件,在主进程层面判断是否允许页面卸载:
主进程代码示例:
let isInitiatingRedirect = false; // 自定义加载页面函数 function loadNewPage(filePath) { isInitiatingRedirect = true; mainWindow.loadFile(filePath); setTimeout(() => { isInitiatingRedirect = false; }, 100); } // 监听渲染进程的beforeunload拦截事件 mainWindow.webContents.on('will-prevent-unload', (e) => { if (isInitiatingRedirect) { e.preventDefault(); // 允许页面卸载,即放行重定向 } });
当主动调用loadNewPage时,主进程会放行页面卸载;用户触发窗口关闭时,will-prevent-unload事件会保持拦截状态。
内容的提问来源于stack exchange,提问作者tinagrit

