如何在Electron同一窗口中同时加载自有应用和YouTube并分左右块展示

实现方案
你之前使用BrowserView无法实现的核心原因是没有配合主窗口的自定义布局,BrowserView本身不自带标题栏渲染能力,需要和主窗口的DOM结构配合使用就能实现你要的左右分栏+双自定义标题栏效果,具体实现步骤如下:
1. 主窗口基础配置
首先创建无边框主窗口,关闭系统自带标题栏,交给你自己实现自定义标题栏:
const { app, BrowserWindow, BrowserView, ipcMain, path } = require('electron') let mainWindow app.whenReady().then(() => { mainWindow = new BrowserWindow({ width: 1200, height: 800, frame: false, // 关闭系统标题栏是实现自定义双标题栏的核心前提 webPreferences: { nodeIntegration: true, contextIsolation: false, preload: path.join(__dirname, 'preload.js') // 安全要求高的场景建议用预加载脚本替代直接开启nodeIntegration } }) mainWindow.loadFile('layout.html') // 主窗口加载本地布局文件 })
2. 编写主窗口布局文件
本地创建layout.html,实现顶部左右双标题栏、中间可拖拽分隔条的基础布局:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> * { margin: 0; padding: 0; box-sizing: border-box; } /* 顶部标题栏总容器,支持拖拽移动窗口 */ .title-bar { height: 40px; display: flex; width: 100%; -webkit-app-region: drag; } /* 左侧YouTube标题栏 */ .yt-title { width: 50%; background: #212121; color: #fff; line-height: 40px; padding: 0 16px; } /* 右侧自有应用标题栏 */ .app-title { flex: 1; background: #ffffff; color: #333; line-height: 40px; padding: 0 16px; border-left: 1px solid #eee; } /* 标题栏操作按钮,取消拖拽属性保证可点击 */ .action-btn { -webkit-app-region: no-drag; float: right; margin-left: 8px; width: 24px; height: 24px; line-height: 24px; text-align: center; margin-top: 8px; border: none; background: transparent; color: inherit; cursor: pointer; border-radius: 4px; } .action-btn:hover { background: rgba(255,255,255,0.1); } .app-title .action-btn:hover { background: rgba(0,0,0,0.1); } /* 可拖拽分隔条,用来调整左右栏宽度 */ .resizer { width: 4px; background: #eee; cursor: col-resize; -webkit-app-region: no-drag; position: absolute; left: 50%; top: 0; bottom: 0; z-index: 10; } </style> </head> <body> <div class="title-bar"> <div class="yt-title"> YouTube <button class="action-btn" onclick="window.close()">×</button> <button class="action-btn" onclick="mainWindow.isMaximized() ? mainWindow.unmaximize() : mainWindow.maximize()">□</button> <button class="action-btn" onclick="mainWindow.minimize()">_</button> <button class="action-btn" onclick="ipcRenderer.send('yt-reload')">↻</button> </div> <div class="app-title"> 自有应用 <button class="action-btn" onclick="ipcRenderer.send('app-back')">←</button> </div> </div> <div class="resizer" id="resizer"></div> <script> const { ipcRenderer, remote } = require('electron') const mainWindow = remote.getCurrentWindow() const resizer = document.getElementById('resizer') const ytTitle = document.querySelector('.yt-title') let isResizing = false // 分隔条拖拽逻辑 resizer.addEventListener('mousedown', (e) => { isResizing = true document.addEventListener('mousemove', handleResize) document.addEventListener('mouseup', () => { isResizing = false document.removeEventListener('mousemove', handleResize) ipcRenderer.send('adjust-view-size', ytTitle.offsetWidth) }) }) function handleResize(e) { if (!isResizing) return const newLeftWidth = e.clientX // 限制左右栏最小宽度为300px,避免布局错乱 if (newLeftWidth < 300 || newLeftWidth > window.innerWidth - 300) return ytTitle.style.width = `${newLeftWidth}px` resizer.style.left = `${newLeftWidth}px` } </script> </body> </html>
3. 创建双BrowserView并绑定布局
主进程中主窗口加载完成后,创建两个BrowserView分别加载YouTube和自有应用,并且监听窗口大小变化、分隔条拖拽事件动态调整两个View的尺寸:
// 接之前主窗口初始化的代码 mainWindow.webContents.on('did-finish-load', () => { // 创建YouTube对应的BrowserView const ytView = new BrowserView({ webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true // 第三方页面建议开启沙箱保证安全 } }) ytView.webContents.loadURL('https://www.youtube.com') mainWindow.addBrowserView(ytView) // 创建自有应用对应的BrowserView const appView = new BrowserView({ webPreferences: { // 根据自有应用的需求配置对应权限 nodeIntegration: true, contextIsolation: false } }) appView.webContents.loadURL('http://localhost:3000') // 替换为你自有应用的地址 mainWindow.addBrowserView(appView) // 统一的视图尺寸调整方法 const adjustViewSize = (leftWidth = 600) => { const [winWidth, winHeight] = mainWindow.getSize() ytView.setBounds({ x: 0, y: 40, // 避开顶部40px的自定义标题栏区域 width: leftWidth, height: winHeight - 40 }) appView.setBounds({ x: leftWidth, y: 40, width: winWidth - leftWidth, height: winHeight - 40 }) } // 初始化视图尺寸 adjustViewSize() // 监听主窗口大小变化,自动调整视图尺寸 mainWindow.on('resize', () => adjustViewSize()) // 监听分隔条拖拽事件,调整视图尺寸 ipcMain.on('adjust-view-size', (e, leftWidth) => adjustViewSize(leftWidth)) // 监听标题栏操作按钮事件 ipcMain.on('yt-reload', () => ytView.webContents.reload()) ipcMain.on('app-back', () => appView.webContents.goBack()) })
注意事项
- 如果对安全要求较高,不要直接在主窗口开启
nodeIntegration: true,改用预加载脚本暴露需要的IPC能力即可 - BrowserView的层级比主窗口DOM层级高,所以标题栏和分隔条的z-index不需要太高,本身就在BrowserView之上不会被遮挡
- 如果需要实现两个区域的独立滚动、独立上下文,BrowserView是官方推荐的最优方案,比webview标签性能和隔离性更好
内容的提问来源于stack exchange,提问作者ptimer
相关产品推荐
相关产品推荐

