Electron 30 把用了多年的 BrowserView 退役了,替代品是 WebContentsView + BaseWindow。不是革新性新能力,是回归 Chromium Views API 标准的工程清理。本文给 4 方对比 + 4 处迁移 diff + 3 个真实迁移坑。
我最近在重写一个老 Electron 应用——它原来用了七八个 BrowserView 拼出"多 tab + 侧边栏 + 浮窗"的界面。结果一升级到 Electron 30,到处都是 deprecated 警告:BrowserView 这个用了多年的 API 终于被换掉了。
替代品是 WebContentsView + BaseWindow 这套新组合。Electron 官方有迁移指南,但偏简略,我自己边写边踩了几个坑——这篇把新模型的设计意图、迁移细节、容易踩的地方一次讲完,省得后来者再走一遍。
为什么要换:BrowserView 的历史包袱
BrowserView 是 Electron 早期为了"在 BrowserWindow 里嵌入子页面"做的一个变通方案。从 Chromium 的视角看它一直是个怪胎——既不是标准的 <iframe>,也不是 Chromium Views API 的标准产物,而是 Electron 自己实现的一层胶水。
具体表现:
- BrowserView 不在 DOM 树里,但它的 webContents 又是完整的 Chromium 渲染管线产物
- BrowserView 的定位、尺寸、层级,全靠主进程显式调用
setBounds管理——和 BrowserWindow 的关系是"挂载"而不是"组合" - 多个 BrowserView 叠加时层级管理混乱,需要手动
setTopBrowserView
这套设计支撑 Electron 跑了好几年,但维护成本越来越高——尤其当 Chromium 自己在演进 Views API 时,BrowserView 这个非标实现总是要单独适配。Electron 团队决定推倒重来:让"嵌入式视图"这件事回归 Chromium Views API 标准路径。
新模型:BaseWindow + WebContentsView
WebContentsView 是 Chromium Views API 的一等公民。它做的事和 BrowserView 类似——在主进程里持有一个 web 渲染单元,由主进程显式控制位置和尺寸——但底层实现完全对接 Chromium Views,未来跟着 Chromium 升级而不用单独适配。
配套的 BaseWindow 是新的窗口基类。BrowserWindow 现在实际上是 BaseWindow + 内置主 WebContentsView 的语法糖;如果你要做更复杂的多视图组合,直接用 BaseWindow 然后手动添加 N 个 WebContentsView。
代码示意:
const { app, BaseWindow, WebContentsView } = require('electron');
app.whenReady().then(() => {
const win = new BaseWindow({ width: 1200, height: 800 });
// 主视图
const mainView = new WebContentsView();
win.contentView.addChildView(mainView);
mainView.setBounds({ x: 0, y: 0, width: 800, height: 800 });
mainView.webContents.loadURL('https://chenguangliang.com');
// 侧边栏视图
const sidebarView = new WebContentsView();
win.contentView.addChildView(sidebarView);
sidebarView.setBounds({ x: 800, y: 0, width: 400, height: 800 });
sidebarView.webContents.loadFile('sidebar.html');
// 浮动小窗
const popupView = new WebContentsView();
win.contentView.addChildView(popupView);
popupView.setBounds({ x: 500, y: 600, width: 300, height: 150 });
popupView.webContents.loadFile('popup.html');
});
三个独立的 WebContentsView 组合在一个 BaseWindow 里,每个有自己独立的 webContents——这意味着独立的 V8 上下文、独立的 cookie 域(如果你配置不同的 partition)、独立的 DevTools。
这种"主进程组合多个视图"的能力,原来要靠多个 BrowserView 凑出来,现在是 first-class API。
与 iframe / webview / BrowserView 的真实差异
新人最容易混淆的是这几个方案的边界。我整理一下:
| 方案 | 在 DOM 中? | 由谁控制 | 进程隔离 | 适用场景 |
|---|---|---|---|---|
<iframe> | ✅ | renderer | 同进程(同域)/ 跨进程(跨域 + site-isolation) | 嵌入站内或可信第三方页面 |
<webview> | ✅ | renderer | 独立进程 | 嵌入完全不可信的第三方页面 |
BrowserView(旧) | ❌ | 主进程 | 独立进程 | 主进程拼多个原生视图 |
WebContentsView(新) | ❌ | 主进程 | 独立进程 | 主进程拼多个原生视图,BrowserView 的现代替代 |
WebContentsView 接替了 BrowserView 的位置——iframe / webview 那两个没变。如果你应用里用的是 iframe 或 webview,不需要迁移。
什么时候选 WebContentsView 而不是 iframe?我的判断:
- 要嵌入完全独立的 web 页面(比如自家 SaaS web 端 + 桌面壳)→ WebContentsView
- 要做多 tab / 多面板的 IDE 类应用(VS Code、Cursor 这种)→ WebContentsView
- 要给嵌入页面单独的 cookie / session / 扩展集合 → WebContentsView(用不同 partition)
- 要嵌入 trusted 站内内容(设置页、模态层) → iframe 简单够用
- 要嵌入完全不可信第三方 → webview 沙箱更彻底
迁移代码:从 BrowserView 改到 WebContentsView
实际迁移大约 80% 是机械替换,剩下 20% 是几个 API 改动。
改动 1:构造和添加方式
// 旧(BrowserView)
const view = new BrowserView({ webPreferences: { ... } });
mainWindow.setBrowserView(view); // 设为主 view
// 或
mainWindow.addBrowserView(view); // 添加额外 view
// 新(WebContentsView)
const view = new WebContentsView({ webPreferences: { ... } });
mainWindow.contentView.addChildView(view); // 统一用 addChildView
注意:setBrowserView 和 addBrowserView 的语义差别被消除了。新 API 全部走 contentView.addChildView()——这是 Views API 标准模式。
改动 2:自动尺寸跟随
// 旧(BrowserView 的 setAutoResize)
view.setAutoResize({ width: true, height: true });
// 新(手动监听 resize 事件)
mainWindow.on('resize', () => {
const [w, h] = mainWindow.getContentSize();
view.setBounds({ x: 0, y: 0, width: w, height: h });
});
这是迁移里最容易漏掉的一步。setAutoResize 没了,要自己处理 resize 事件。如果你的应用有侧边栏 + 主视图 + 底栏的复杂布局,建议封装一个 LayoutManager 类管理所有 view 的 bounds——比散在各处 resize listener 好维护。
改动 3:transparent 背景
// 旧
view.setBackgroundColor('#00000000'); // 部分平台不生效
// 新
view.setBackgroundColor('#00000000'); // alpha=00 全平台生效
代码一样,但据我自己实测,新 API 在跨平台透明背景上的表现比旧 BrowserView 更稳——原来 Windows 上偶尔不生效的问题在我的项目里消失了(具体修复细节未在官方 changelog 看到明确条目,按你的实测为准)。
改动 4:webContents 访问
// 旧 + 新都一样
view.webContents.loadURL(...);
view.webContents.openDevTools();
view.webContents 这个属性没变——你之前所有 IPC 通信、监听器、preload 配置都不用动。这是迁移成本最低的一档。
几个我踩过的坑
光读官方迁移指南是不够的。我踩了三个坑,记下来给后来者:
坑 1:addChildView 的顺序就是层级
addChildView 后加的会盖在先加的上面(Z-index 顺序)。所以浮动小窗 / 模态层要最后加。如果你迁移时按"主视图 → 侧边栏 → 模态浮窗"的顺序,巧合是对的;但如果按"模态 → 主视图 → 侧边栏"的顺序加,模态会被盖到最底下。
旧的 setTopBrowserView 没了,要重排顺序得 removeChildView + addChildView 重新加。
坑 2:DevTools 现在独立窗口
旧 BrowserView 的 DevTools 默认是 docked 模式。WebContentsView 的 DevTools 默认会单独开新窗口——这不是 bug 是 feature,但用习惯了的话第一次会懵。要 docked 模式:
view.webContents.openDevTools({ mode: 'detach' }); // 独立窗口
view.webContents.openDevTools({ mode: 'bottom' }); // 底部 docked
坑 3:preload 路径一律用绝对路径
这条不是 WebContentsView 特有的——Electron 一直要求 webPreferences.preload 必须是绝对路径。但旧 BrowserView 代码里如果你侥幸用了相对路径(某些版本下有兜底解析行为),迁移时建议统一改成 path.join(__dirname, 'preload.js')。这条算迁移时顺手清理的技术债,不算 WebContentsView 引入的新问题。
性能:会变更好吗
诚实回答:单视图场景几乎没差别。WebContentsView 的实际渲染管线和 BrowserView 一样底层用 Chromium,性能差异在 1% 以内。
差异在多视图场景:
- 旧 BrowserView 多视图叠加时主进程要做大量 z-index 计算和 IPC 协调
- 新 WebContentsView 走 Chromium Views API 标准合成路径,多视图叠加效率更高
实测我那个有 7 个视图的老应用迁移后:内存占用约降 8%,启动速度约快 200ms。样本只有一个项目,仅供参考——不是颠覆性提升,但白送的优化值得拿。
给还没迁移的人的建议
如果你现在还在 Electron < 30,迁移时机看产品节奏:
- 明年才升 Electron 主版本:不急,先继续用 BrowserView,但写新代码尽量用 WebContentsView 避免增加迁移债
- 本季度要升 Electron 30+:现在就把迁移做完,deprecated 警告不解决迟早会成阻塞项
- 本来就在 Electron 30+ 但还在用 BrowserView:抓紧迁——Electron 官方未公布具体移除版本,但按 deprecated → 移除通常 2-4 个大版本的节奏看,建议 Electron 32 之前清干净
迁移本身工作量不大,一个有 5-10 个 view 的中型项目大约半天到一天能搞定。真正的成本是测试——多视图布局的边角 case 很多,建议把所有 view 切换、resize、模态弹出的场景过一遍。
一句话总结
WebContentsView 不是革新性的新能力,是 Electron 把 BrowserView 这套自家实现退役、回归 Chromium Views API 标准的工程清理。对开发者:API 更一致、未来更稳定、多视图组合性能略好;对维护者:少了一层非标胶水,跟着 Chromium 升级少踩坑。
不算性感的更新,但是把基础设施收拾干净的那种——值得迁,但不用激动。
延伸阅读:
- Electron 官方迁移指南:BrowserView → WebContentsView - 最权威的 API 对照
- WebContentsView API 文档 - 接口完整定义
- BaseWindow API 文档 - 新窗口基类
- Web Embeds 选择指南 - iframe / webview / WebContentsView 三方对比
- PR #35658: 用 WebContentsView 替换 BrowserView - 设计动机和讨论
- Flutter Desktop vs Electron(本博客 blog172) - 跨框架视角看 Electron 多视图能力
原文首发于 chenguangliang.com