Electron中BrowserView卡顿后无法用removeBrowserView移除的问题
问题:Electron中卡顿的BrowserView无法被主进程移除
我正在开发一款Electron应用,通过BrowserView托管外部网页作为子视图,模拟Chrome单窗口多标签的模式。目前遇到问题:当某个BrowserView(如stuckme1)因无限循环打印console.log导致卡顿时,主进程无法使用removeBrowserView方法将其从主BrowserWindow中移除,该问题仅在视图滥用内存导致卡顿时出现。
5秒后,stuckme.html会卡住,此时主进程无法切换到stuckme2.html,无报错但内存占用持续升高。需要实现即使视图中存在不良代码,也能保持应用响应的解决方案。
相关代码
主进程index.ts
const createWindow = (): void => { // Create the browser window. const mainWindow = new BrowserWindow({ height: 600, width: 800, webPreferences: { preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, }, }); // and load the index.html of the app. mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY); const stuckme1 = new BrowserView(); stuckme1.webContents.loadURL(STUCKME_WEBPACK_ENTRY); stuckme1.setBounds({ x: 0, y: 0, height: 200, width: 200 }); const stuckme2 = new BrowserView(); stuckme2.webContents.loadURL(STUCKME2_WEBPACK_ENTRY); stuckme2.setBounds({ x: 0, y: 0, height: 200, width: 200 }); let toggle = true; setInterval(() => { if (toggle) { mainWindow.removeBrowserView(stuckme1); mainWindow.addBrowserView(stuckme2); } else { mainWindow.removeBrowserView(stuckme2); mainWindow.addBrowserView(stuckme1); } toggle = !toggle; }, 1000); };
stuckme.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Hello World!</title> <style> body { border: 1px solid green; } </style> <script type="text/javascript"> setInterval(() => { document.querySelector(".counter").innerHTML = Number(document.querySelector(".counter").innerHTML) + 1 }, 1000); setTimeout(() => { while (true) { console.log("stuck"); } }, 5000); </script> </head> <body> <p>Stuck me</p> <span class="counter"></span> </body> </html>
stuckme2.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Hello World!</title> <style> body { border: 1px solid red; } </style> <script type="text/javascript"> setInterval(() => { document.querySelector(".counter").innerHTML = Number(document.querySelector(".counter").innerHTML) + 1 }, 1000); </script> </head> <body> <p>Stuck me2</p> <span class="counter"></span> </body> </html>
解决方案
1. 强制终止无响应的渲染进程
监听BrowserView的unresponsive事件,一旦检测到渲染进程卡死,先调用destroy()彻底终止进程,再执行视图移除和切换操作,避免主进程被阻塞:
stuckme1.webContents.on('unresponsive', () => { stuckme1.webContents.destroy(); mainWindow.removeBrowserView(stuckme1); mainWindow.addBrowserView(stuckme2); }); stuckme2.webContents.on('unresponsive', () => { stuckme2.webContents.destroy(); mainWindow.removeBrowserView(stuckme2); mainWindow.addBrowserView(stuckme1); });
2. 启用渲染进程沙箱模式
创建BrowserView时开启沙箱和上下文隔离,限制渲染进程权限,隔离卡顿/崩溃对主进程和其他视图的影响:
const stuckme1 = new BrowserView({ webPreferences: { sandbox: true, contextIsolation: true, nodeIntegration: false } }); const stuckme2 = new BrowserView({ webPreferences: { sandbox: true, contextIsolation: true, nodeIntegration: false } });
3. 超时兜底的安全移除逻辑
给视图移除操作添加超时兜底,超时后强制销毁进程,确保切换逻辑能执行:
const removeViewSafely = (window: BrowserWindow, view: BrowserView): Promise<void> => { return new Promise(resolve => { const timeout = setTimeout(() => { view.webContents.destroy(); resolve(); }, 1000); // 1秒超时后强制销毁 try { window.removeBrowserView(view); clearTimeout(timeout); resolve(); } catch (e) { clearTimeout(timeout); view.webContents.destroy(); resolve(); } }); }; // 修改定时器逻辑为异步 let toggle = true; setInterval(async () => { if (toggle) { await removeViewSafely(mainWindow, stuckme1); mainWindow.addBrowserView(stuckme2); } else { await removeViewSafely(mainWindow, stuckme2); mainWindow.addBrowserView(stuckme1); } toggle = !toggle; }, 1000);
4. 捕获渲染进程崩溃事件
监听crashed事件,在进程崩溃时及时清理视图并切换到正常视图:
stuckme1.webContents.on('crashed', () => { mainWindow.removeBrowserView(stuckme1); mainWindow.addBrowserView(stuckme2); }); stuckme2.webContents.on('crashed', () => { mainWindow.removeBrowserView(stuckme2); mainWindow.addBrowserView(stuckme1); });
内容的提问来源于stack exchange,提问作者Itay Elkouby
相关产品推荐
相关产品推荐

