BrowserWindow 详解
本教程共 45 篇 · 第 7 篇 · 更新于 2026-08-03
7. BrowserWindow 详解
本节目标
- 掌握 BrowserWindow 的构造选项:width/height 与尺寸约束。
- 理解 webPreferences 中 contextIsolation、nodeIntegration、sandbox 的安全含义。
- 使用 frame、transparent、alwaysOnTop 等选项定制窗口外观。
- 用 loadFile 与 loadURL 分别加载本地与远程内容。
- 认识 BaseWindow 及其与 BrowserWindow 的关系。
窗口是用户直接接触的界面。BrowserWindow 是创建窗口的主类,构造时的一堆选项决定它长什么样、怎么动。本章把最常用的选项讲清楚,并认识它的底层基类 BaseWindow。
7-1 创建窗口的基本写法
BrowserWindow 在 app 的 ready 之后才能用。最朴素的创建只需给宽高,其余走默认值。
// 主进程
const { BrowserWindow } = require('electron/main')
const win = new BrowserWindow({ width: 800, height: 600 })
win.loadFile('index.html')
width 默认 800、height 默认 600。想居中显示可加 center: true,想固定初始位置用 x 和 y(这两个必须成对出现)。
7-2 常用尺寸与约束选项
除了宽高,还有一组控制窗口边界的选项。它们约束的是用户,不约束你代码里 setBounds 传的值。
const win = new BrowserWindow({
width: 1000,
height: 700,
minWidth: 400, // 用户不能拖得更窄
minHeight: 300,
maxWidth: 1600,
maxHeight: 1000,
resizable: true, // 是否允许缩放,默认 true
center: true
})
resizable 设为 false 会锁死大小,配合固定宽高能做出不可调的工具窗。注意 minWidth 等只是限制用户拖拽,代码内部仍可越界。
7-3 webPreferences:安全相关的核心
webPreferences 是窗口最关键的一组配置,直接关系安全。contextIsolation、nodeIntegration、sandbox 都在这里设。
// 主进程
const path = require('node:path')
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // v43 默认即 true,保持
nodeIntegration: false, // 默认 false,不要把 Node 暴露给页面
sandbox: true // 按需求开启沙箱
}
})
contextIsolation: true 和 nodeIntegration: false 是 v43 的默认值,写在这里是显式声明,提醒后来人。切勿把它们改成 false。
sandbox: true 会让渲染进程跑在 Chromium 沙箱里,进一步限制 preload 可用 API。对加载远程内容的窗口,开启它更稳妥。
7-4 无边框与透明窗口
frame: false 去掉系统标题栏和边框,做出自定义标题栏的应用。透明窗口则靠 transparent: true 实现毛玻璃或异形界面。
// 主进程:无边框 + 透明,常用来做异形窗口
const win = new BrowserWindow({
width: 400,
height: 300,
frame: false, // 无边框
transparent: true, // 背景透明
resizable: false // 透明窗口不支持缩放
})
提示:透明窗口有几条官方明说的限制。它不能被点击穿透,也不可调整大小(把 resizable 设为 true 可能让透明直接失效);打开 DevTools 时窗口会变回不透明;CSS 的 blur() 只作用于页面自身,毛玻璃透不到窗口下方的其他应用。Windows 上 DWM 被禁用时透明会失效,且这类窗口无法用系统菜单或双击标题栏最大化;macOS 上则不显示原生窗口阴影。
无边框窗口要自己实现拖拽和关闭按钮,通常用 CSS 的 -webkit-app-region: drag 标记可拖拽区域,细节放到窗口定制章节展开。
7-5 置顶、全屏与模态
alwaysOnTop 让窗口永远浮在其他窗口之上,常见于音乐播放器迷你窗。fullscreen 控制全屏,modal 配合 parent 做出阻塞式对话框。
const child = new BrowserWindow({
width: 400,
height: 300,
parent: mainWin, // 指定父窗口
modal: true, // 模态:阻塞父窗口交互
alwaysOnTop: false
})
modal: true 仅在指定了 parent 时生效。模态子窗口不关闭,父窗口就点不动,适合确认弹窗这类场景。
7-6 加载本地与远程内容
窗口内容两种来源:本地文件用 loadFile,远程地址用 loadURL。前者是桌面应用的主流,后者要特别小心安全。
// 加载本地页面(推荐)
win.loadFile('index.html')
// 加载远程地址(需评估风险)
win.loadURL('https://example.com')
加载远程内容时,页面可能含不可信脚本。务必保持 contextIsolation: true 与 nodeIntegration: false,不要给这种窗口开 sandbox: false,更不要暴露多余 API。
7-7 优雅显示:避免白屏闪烁
页面直接加载时,用户可能看到内容一点点刷出来。用 ready-to-show 事件等首次渲染完再 show,能消除闪烁。
const win = new BrowserWindow({ show: false })
win.once('ready-to-show', () => {
win.show()
})
对加载资源多的页面,ready-to-show 可能来得太晚。这时给个接近应用底色的 backgroundColor 更实用,让用户感觉更原生。
const win = new BrowserWindow({ backgroundColor: '#2e2c29' })
win.loadURL('https://github.com')
7-8 认识 BaseWindow
BrowserWindow 其实是 BaseWindow 的子类。BaseWindow 提供底层窗口能力,适合在一个窗口里组合多个 WebContentsView 视图。
// 主进程
const { BaseWindow, WebContentsView } = require('electron/main')
const win = new BaseWindow({ width: 800, height: 600 })
const left = new WebContentsView()
left.webContents.loadURL('https://electronjs.org')
left.setBounds({ x: 0, y: 0, width: 400, height: 600 })
win.contentView.addChildView(left)
对只有一个整页视图的普通应用,BrowserWindow 更简单。需要多视图拼合(如分栏、画中画)时,才考虑 BaseWindow 加 WebContentsView。
7-9 窗口事件:感知状态变化
BrowserWindow 本质是事件发射器,你能监听它的各种状态。比如页面加载完成、失去焦点、进入全屏,都能拿到回调。
win.on('focus', () => {
console.log('窗口获得焦点')
})
win.on('blur', () => {
console.log('窗口失去焦点')
})
这些事件常用来做”切到后台就暂停动画""获得焦点就刷新数据”之类的行为。常用的还有 show/hide、maximize/unmaximize、enter-full-screen。
7-10 常见误区
最危险的一个错误,是把 nodeIntegration 设成 true 让页面直接用 Node。这等于撕开安全边界,渲染进程一旦加载了不可信内容就会出事。正确做法始终是用 preload 暴露少量接口。
另一个误会没那么严重,但很常见:以为 minWidth/maxWidth 能限制代码里 setBounds 传的值。这些选项只约束用户拖拽,你自己在代码里依然能越界设置,别指望它们当万能锁。
7-11 实战小技巧
记住窗口位置是最实用的一个。关闭前把 win.getBounds() 存进配置,下次启动用存的值建窗口,用户体验会连贯很多。
// 主进程;saveBounds 是你自己实现的存盘函数
app.on('before-quit', () => {
const bounds = win.getBounds()
saveBounds(bounds)
})
任务栏也有文章可做。win.setProgressBar(0.5) 会在任务栏图标上画一条进度条,下载类应用常用;win.flashFrame(true) 让图标闪烁,用来提醒用户有消息到达。
这些 API 都挂在 BrowserWindow 实例上,配合生命周期事件就能做出贴近原生的细节。窗口的玩法远不止建一个框,值得在文档里慢慢翻。
7-12 尺寸单位与高分屏
窗口的 width、height 单位是 CSS 像素,不是物理像素。在高分屏(如 Retina)上,Electron 会自动按设备的 devicePixelRatio 缩放,界面不会模糊。
这表示你写的 800×600,在 2 倍屏上实际占据更多物理像素,但代码里无需特殊处理。只有用到 webContents 截图或原生图像时,才需要手动乘上缩放比。
如果你做的是设计类应用,想拿到真实物理分辨率,可以读 screen 模块的 getPrimaryDisplay().scaleFactor。普通业务窗口基本不用管,交给 Electron 默认处理即可。
7-13 多显示器与窗口位置
当用户有多个显示器,新建窗口默认出现在主屏中央。想指定屏幕,可先用 screen 模块拿到某块显示器的工作区,再把窗口挪过去。
// 主进程:screen 模块必须在 app 就绪之后才能用
const { app, screen, BrowserWindow } = require('electron/main')
app.whenReady().then(() => {
const external = screen.getAllDisplays().find(d => d.bounds.x !== 0)
const win = new BrowserWindow({ width: 800, height: 600 })
if (external) {
win.setBounds(external.workArea)
}
win.loadFile('index.html')
})
workArea 是扣除任务栏后的可用区域,比 bounds 更合适放窗口。记住传入 x 时必须同时传 y,否则定位不生效。多屏应用里这套逻辑很常用。
7-14 本章小结
BrowserWindow 的构造选项决定窗口外观与行为:尺寸约束、webPreferences 安全开关、无边框透明、置顶模态,以及本地/远程加载。安全选项保持默认开隔离、关 Node 集成。
优雅显示用 ready-to-show 或 backgroundColor,多视图布局可下沉到 BaseWindow。下一章进入多窗口管理。