Electron BrowserWindow 完全指南:创建、控制与深度定制跨平台桌面窗口
BrowserWindow 是 Electron 主进程中最核心的窗口类,负责创建和控制应用程序的浏览器窗口,涵盖窗口生命周期管理、尺寸位置控制、全屏/最大化/最小化、任务栏集成、平台专属外观(如 macOS 毛玻璃与标签页、Windows 强调色与缩略工具栏)等全部能力。本文以 Electron 官方 API 文档 browser-window.md 为主体,结合仓库中的 TypeScript 胶水层源码与 C++ 实现,系统讲解该类的事件、属性、方法与构造选项,帮助你在实际项目中写出行为正确、跨平台一致的窗口代码。
快速上手:创建窗口并加载页面
BrowserWindow 模块只能在 app 模块发出 ready 事件之后使用。创建窗口并加载远程地址或本地 HTML 的最小示例如下:
// In the main process.
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ width: 800, height: 600 })
// Load a remote URL
win.loadURL('https://github.com')
// Or load a local HTML file
win.loadFile('index.html')
从源码结构看,BrowserWindow 的 JS 层实现位于 browser-window.ts:loadURL、loadFile、reload、send、openDevTools 等方法全部被转发到窗口持有的 webContents 上,因此文档中反复出现的“Same as webContents.xxx”表述是准确的——win.loadURL(url) 等价于 win.webContents.loadURL(url)。
构造选项:BrowserWindowConstructorOptions
new BrowserWindow([options]) 接受一个可选的 options 对象,类型为 BrowserWindowConstructorOptions,它继承自 BaseWindowConstructorOptions。BrowserWindow 专属的选项只有两个:
| 选项 | 类型 | 说明 |
|---|---|---|
webPreferences |
WebPreferences | 设置网页功能(Node 集成、上下文隔离等) |
paintWhenInitiallyHidden |
boolean | show 为 false 且刚创建时渲染器是否活跃。为使 show: false 时 document.visibilityState 在首次加载时工作正确,应设为 false;设为 false 会导致 ready-to-show 事件永不触发。默认 true |
继承自基类的常用选项(完整列表见 base-window-options.md):
| 选项 | 类型/默认值 | 说明 |
|---|---|---|
width / height |
Integer,默认 800 / 600 |
窗口宽高(像素) |
x / y |
Integer | 窗口相对屏幕的偏移;使用其中一个时另一个必填,默认居中 |
useContentSize |
boolean,默认 false |
宽高按网页内容区计算,实际窗口会因边框略大 |
center |
boolean,默认 false |
屏幕居中显示 |
minWidth/minHeight/maxWidth/maxHeight |
Integer | 最小/最大尺寸。注意:这些约束只限制用户,不会阻止你通过构造函数或 setBounds/setSize 传入超出约束的尺寸 |
resizable / movable / minimizable / maximizable / closable |
boolean,默认 true |
窗口能力开关(后四项在 Linux 上未实现) |
focusable |
boolean,默认 true |
窗口是否可获得焦点;Windows 上 false 隐含 skipTaskbar: true |
alwaysOnTop |
boolean,默认 false |
是否始终置顶;Wayland(Linux)不支持 |
fullscreen / fullscreenable / simpleFullscreen |
boolean,默认 false/true/false |
全屏相关;fullscreen: false 时在 macOS 会隐藏或禁用全屏按钮 |
kiosk |
boolean,默认 false |
是否处于 kiosk 模式 |
name |
string | 窗口唯一标识,用于状态持久化等内部功能,被销毁前不可复用 |
windowStatePersistence |
boolean 或配置对象(实验性) | 跨启动持久化窗口位置、尺寸、最大化状态;需配合 name |
title |
string,默认 "Electron" |
默认标题;若 HTML 的 <title> 存在则被忽略 |
icon |
NativeImage 或 string | 窗口图标,Windows 建议用 ICO |
show |
boolean,默认 true |
创建时是否显示 |
frame |
boolean,默认 true |
设为 false 创建无边框窗口(自定义窗口样式) |
parent |
BaseWindow | 父窗口;modal 仅在子窗口中生效 |
modal |
boolean,默认 false |
是否为模态窗口 |
acceptFirstMouse |
boolean(macOS) | 点击非激活窗口是否直接点击穿透到网页内容 |
autoHideMenuBar |
boolean(Linux/Windows) | 菜单栏自动隐藏,按 Alt 显示 |
backgroundColor |
string,默认 #FFF |
窗口背景色,支持 Hex/RGB/RGBA/HSL/HSLA/命名色 |
hasShadow / opacity |
boolean 默认 true / number 0.0~1.0 |
阴影与不透明度(opacity 仅 Windows/macOS) |
darkTheme |
boolean,默认 false |
强制深色主题(仅部分 GTK+3 桌面环境) |
transparent |
boolean,默认 false |
透明窗口;Windows 上无边框才生效 |
type |
string | 平台相关窗口类型:Linux 有 desktop/dock/toolbar/splash/notification,macOS 有 desktop/textured(已弃用)/panel,Windows 有 toolbar |
titleBarStyle |
string,默认 default |
标题栏样式:default、hidden(配合 titleBarOverlay: true 启用 Windows/Linux 的 Window Controls Overlay)、hiddenInset(macOS)、customButtonsOnHover(macOS,实验性) |
titleBarOverlay |
boolean/Object,默认 false |
启用 Window Controls Overlay,可配 color、symbolColor、height |
accentColor |
boolean/string(Windows) | 任务栏强调色,false 显式禁用,颜色字符串自定义,Alpha 会被忽略 |
trafficLightPosition |
Point(macOS) | 无边框窗口红绿灯按钮的自定义位置 |
roundedCorners |
boolean,默认 true |
无边框窗口圆角;Windows 11 Build 22000 以下无效 |
thickFrame |
boolean(Windows),默认 true |
无边框窗口使用 WS_THICKFRAME,去掉后阴影、动画、拖拽缩放均失效 |
vibrancy |
string(macOS) | 毛玻璃效果类型,如 appearance-based、titlebar、sidebar、hud 等 |
backgroundMaterial |
string(Windows) | 系统绘制背景材质:auto/none/mica/acrylic/tabbed |
zoomToPageWidth |
boolean(macOS),默认 false |
点击绿色停止灯按钮时按页面宽度还是屏幕宽度缩放 |
tabbingIdentifier |
string(macOS) | 标签组名称,相同标识的窗口按原生标签分组,并触发 new-window-for-tab 事件 |
更多窗口外观定制(无边框、透明窗口、自定义标题栏)可参考 Window Customization 教程。
优雅地显示窗口,避免视觉闪烁
直接在窗口中加载页面时,用户可能看到页面逐步渲染出来的过程,这对原生应用体验并不好。文档给出两种针对不同场景的解决方案。
方案一:使用 ready-to-show 事件
加载页面期间,如果窗口尚未显示,当渲染进程第一次渲染出页面时会发出 ready-to-show 事件,此时再调用 show() 就没有视觉闪烁:
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ show: false })
win.once('ready-to-show', () => {
win.show()
})
这个事件通常晚于 did-finish-load 事件发出,但对于包含大量远程资源的页面,也可能先于 did-finish-load 发出。
从 C++ 源码看,该事件的触发点在 electron_api_web_contents.cc 的 OnFirstNonEmptyLayout 回调中:只有当主框架(PrimaryMainFrame)完成首次非空布局时才 Emit("ready-to-show")。这意味着它比“加载完成”更接近“用户能看到内容”的时机。
需要注意:使用该事件隐含了即使 show 为 false,渲染器也会被视为“可见”并参与绘制;如果设置了 paintWhenInitiallyHidden: false,该事件将永不触发。
方案二:设置 backgroundColor 属性
对于复杂应用,ready-to-show 可能触发过晚,让应用显得迟缓。此时建议立即显示窗口,并设置一个接近应用背景色的 backgroundColor:
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ backgroundColor: '#2e2c29' })
win.loadURL('https://github.com')
文档特别指出:即使用了 ready-to-show,也建议设置 backgroundColor 让应用感觉更像原生应用。合法的取值示例:
const win = new BrowserWindow()
win.setBackgroundColor('hsl(230, 100%, 50%)')
win.setBackgroundColor('rgb(255, 145, 145)')
win.setBackgroundColor('#ff00a3')
win.setBackgroundColor('blueviolet')
完整格式规则见下文 win.setBackgroundColor 一节。
父子窗口与模态窗口
通过 parent 选项可以创建子窗口:
const { BrowserWindow } = require('electron')
const top = new BrowserWindow()
const child = new BrowserWindow({ parent: top })
child.show()
top.show()
child 窗口将始终显示在 top 窗口之上。运行时还可以用 win.setParentWindow(parent) 修改父窗口(传 null 恢复为顶层窗口),用 win.getParentWindow() 查询父窗口、win.getChildWindows() 获取全部子窗口。
模态窗口
模态窗口是一种会禁用父窗口的子窗口,必须同时设置 parent 和 modal 两个选项:
const { BrowserWindow } = require('electron')
const top = new BrowserWindow()
const child = new BrowserWindow({ parent: top, modal: true, show: false })
child.loadURL('https://github.com')
child.once('ready-to-show', () => {
child.show()
})
运行时可用 win.isModal() 判断当前窗口是否为模态窗口。
页面可见性(Page Visibility)
BrowserWindow 与 [Page Visibility API] 的对应行为如下:
- 所有平台上,可见性状态跟踪窗口是否被隐藏/最小化;
- 此外在 macOS 上还跟踪遮挡(occlusion)状态:窗口被其他窗口完全覆盖时状态为
hidden。其他平台只有窗口最小化或用win.hide()显式隐藏时才为hidden; - 若窗口以
show: false创建,初始可见性状态仍是visible,尽管窗口实际未显示; - 若禁用了
backgroundThrottling,即使窗口被最小化、遮挡或隐藏,可见性状态也会保持visible。
建议:当可见性状态为 hidden 时暂停昂贵操作,以最小化功耗。
从源码结构看,JS 层在 browser-window.ts 中订阅了 show、hide、minimize、maximize、restore 事件,通过 isVisible() && !isMinimized() 计算状态变化后,向 webContents 发出内部事件 -window-visibility-change,再传导到渲染进程的 document.visibilityState。仓库测试 visibility-state-spec.ts 覆盖了“默认可见”“初始隐藏”“加载前显示/隐藏”等组合场景,验证了上述行为矩阵。
平台差异提示
跨平台开发时需要牢记以下差异:
- macOS 上模态窗口显示为附着在父窗口上的 sheet;
- macOS 上父窗口移动时子窗口保持相对位置,而 Windows 和 Linux 上子窗口不随父窗口移动;
- Linux 上模态窗口类型会被改为
dialog,且许多桌面环境不支持隐藏模态窗口; - Wayland(Linux)上通常无法在创建后以编程方式调整窗口大小,也无法在未经用户输入的情况下定位、移动、聚焦或取消聚焦窗口。如果应用需要这些能力,可加
--ozone-platform=x11启动标志走 Xwayland 运行。
类结构:BrowserWindow extends BaseWindow
BrowserWindow 继承自 BaseWindow,是 EventEmitter 的扩展类。它按 options 设置的原生属性创建一个新窗口。
警告:Electron 的内置类不能在用户代码中被继承(subclass)。原因可参考 FAQ。
从源码结构看,这一继承关系在 JS 层是显式搭建的:browser-window.ts 通过 process._linkedBinding('electron_browser_window') 获取 C++ 原生类,并用 Object.setPrototypeOf(BrowserWindow.prototype, BaseWindow.prototype) 挂到 BaseWindow 原型链上;base-window.ts 则通过 Object.defineProperty 把 title、fullScreen、kiosk、shadow、resizable、closable 等属性实现为 getter/setter 对,分别代理到 getTitle()/setTitle()、isFullScreen()/setFullScreen() 等实例方法上。
此外,构造函数初始化(_init)还完成了两件重要的事:把窗口的 focus/blur 事件同时转发为 app 实例的 browser-window-focus/browser-window-blur 事件;以及通过 app.emit('browser-window-created') 通告窗口创建(见 browser-window.ts)。
实例事件
由 new BrowserWindow 创建的对象会发出以下事件(部分事件标注了平台限制)。
文档与关闭类
Event: 'page-title-updated'
- 返回:
eventEvent;titlestring;explicitSetboolean - 文档标题变化时触发;调用
event.preventDefault()可阻止原生窗口标题改变。explicitSet为false表示标题是从文件 URL 合成的。
Event: 'close'
- 返回:
eventEvent - 窗口即将关闭时触发,早于 DOM 的
beforeunload和unload事件;event.preventDefault()可取消关闭。通常应使用beforeunload处理器来决定是否允许关闭(它在页面重载时也会被调用)。在 Electron 中,返回任何非undefined值都会取消关闭:
window.onbeforeunload = (e) => {
console.log('I do not want to be closed')
// Unlike usual browsers that a message box will be prompted to users, returning
// a non-void value will silently cancel the close.
// It is recommended to use the dialog API to let the user confirm closing the
// application.
e.returnValue = false
}
注意:
window.onbeforeunload = handler与window.addEventListener('beforeunload', handler)的行为有细微差别。推荐始终显式设置event.returnValue,而不是仅仅返回值,因为前者在 Electron 中表现更一致。
Event: 'closed'
- 窗口已关闭时触发。收到该事件后应移除对窗口的引用,不再使用它。
Windows 会话生命周期
Event: 'query-session-end'(Windows)
- 返回:
eventWindowSessionEndEvent - 系统关机、重启或用户注销导致会话即将结束时触发。
event.preventDefault()可以延迟系统关机——虽然一般应尊重用户结束会话的选择,但当结束会话可能使用户丢失数据时可以考虑使用它。
Event: 'session-end'(Windows)
- 返回:
eventWindowSessionEndEvent - 会话即将结束时触发。该事件触发后,无法再阻止会话结束。
响应性与焦点
Event: 'unresponsive' / Event: 'responsive':网页变得无响应/恢复响应时触发。
源码层面,browser-window.ts 对 webContents 的 unresponsive 事件做了 50ms 的延迟去抖后才发出窗口级 unresponsive;若窗口触发 close 且未被 preventDefault,还会再等待 5 秒未恢复响应才发出——即无响应判定带有防抖设计,避免瞬时卡顿误报。
Event: 'blur' / Event: 'focus':窗口失去/获得焦点时触发。这两个事件会同步转发给 app 实例(见上文 _init 分析)。
显示与窗口状态
| 事件 | 说明 |
|---|---|
show / hide |
窗口被显示/隐藏时 |
ready-to-show |
页面(在未显示状态下)已渲染完毕,窗口可以无闪烁地显示。注意使用该事件隐含渲染器被视为“可见”并绘制;paintWhenInitiallyHidden: false 时永不触发 |
maximize / unmaximize |
窗口最大化/退出最大化 |
minimize |
窗口最小化。Wayland 上“最小化”目前不是受支持的状态,仅在客户端装饰触发(如无边框窗口 Window Controls Overlay 上的最小化按钮)时才触发 |
restore |
从最小化状态恢复 |
调整大小与移动
Event: 'will-resize'(macOS/Windows)
- 返回:
eventEvent;newBoundsRectangle(正在调整到的尺寸);details对象,含edgestring(被拖拽的边:bottom、left、right、top-left、top-right、bottom-left、bottom-right) - 窗口调整大小前触发,
event.preventDefault()可阻止调整。仅在用户手动调整时触发,setBounds/setSize不会触发。edge取值平台相关:Windows 支持全部 7 个值;macOS 只有bottom(垂直缩放)和right(水平缩放)。
Event: 'resize':窗口被调整后触发。
Event: 'resized'(macOS/Windows):窗口完成一次调整时触发一次。通常由手动调整触发;macOS 上带 animate: true 的 setBounds/setSize 完成后也会触发一次。
Event: 'will-move'(macOS/Windows)
- 返回:
eventEvent;newBoundsRectangle(正在移动到的位置) - 窗口移动前触发;Windows 上
event.preventDefault()可阻止移动。仅手动移动时触发,setPosition/setBounds/center不触发。
Event: 'move':窗口正在移动到新位置时触发。
Event: 'moved'(macOS/Windows):窗口移动到新位置完成时触发一次。macOS 上它是 move 的别名。
全屏
| 事件 | 说明 |
|---|---|
enter-full-screen / leave-full-screen |
进入/退出(窗口级)全屏 |
enter-html-full-screen / leave-html-full-screen |
进入/退出由 HTML API(如 requestFullscreen)触发的全屏 |
置顶
Event: 'always-on-top-changed'
- 返回:
eventEvent;isAlwaysOnTopboolean - 窗口被设置或取消“始终置顶”时触发。
输入与手势
Event: 'app-command'(Windows/Linux)
- 返回:
eventEvent;commandstring - 当 App Command 被调用时触发,通常与键盘媒体键、浏览器命令以及部分 Windows 鼠标上的“后退”按钮相关。命令会被小写化、下划线替换为连字符,并去掉
APPCOMMAND_前缀,例如APPCOMMAND_BROWSER_BACKWARD发出为browser-backward。
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
win.on('app-command', (e, cmd) => {
// Navigate the window back when the user hits their mouse back button
if (cmd === 'browser-backward' && win.webContents.canGoBack()) {
win.webContents.goBack()
}
})
Linux 上明确支持以下 app command:browser-backward、browser-forward。
Event: 'swipe'(macOS)
- 返回:
eventEvent;directionstring(up、right、down、left) - 三指滑动手势触发。底层方法面向旧式 macOS 触控板“页面滑动”(屏幕内容不随滑动移动);现在的触控板大多不再允许这种配置,需在
System Preferences > Trackpad > More Gestures中将 “Swipe between pages” 设为 “Swipe with two or three fingers” 才能正常触发。
Event: 'rotate-gesture'(macOS)
- 返回:
eventEvent;rotationFloat - 触控板旋转手势触发,持续触发直到手势结束。每次事件的
rotation是距上次触发的旋转角度(度),手势最后一次事件的值恒为0。逆时针为正,顺时针为负。
Event: 'system-context-menu'(Windows/Linux)
- 返回:
eventEvent;pointPoint(上下文菜单触发的屏幕坐标) - 在窗口上触发系统上下文菜单时发出,通常发生在用户右键点击非客户区(窗口标题栏,或无边框窗口中声明了
-webkit-app-region: drag的区域)时。event.preventDefault()可阻止菜单显示。 - 将
point转换为 DIP 可使用screen.screenToDipPoint(point)(见 screen 文档)。
macOS 专属事件
| 事件 | 说明 |
|---|---|
sheet-begin / sheet-end |
窗口打开/关闭 sheet 时 |
new-window-for-tab |
用户点击 macOS 原生新标签按钮时。仅当窗口设置了 tabbingIdentifier 时按钮可见;必须在该处理器中创建窗口,macOS 标签功能才能按预期工作 |
静态方法
| 方法 | 说明 |
|---|---|
BrowserWindow.getAllWindows() |
返回 BrowserWindow[],所有已打开的浏览器窗口 |
BrowserWindow.getFocusedWindow() |
返回 BrowserWindow | null,当前应用中拥有焦点的窗口 |
BrowserWindow.fromWebContents(webContents) |
返回拥有该 webContents 的窗口,未被窗口拥有时返回 null |
BrowserWindow.fromId(id) |
返回具有给定 id 的窗口,不存在时为 null |
BrowserWindow.fromBrowserView(browserView)(已弃用) |
返回拥有该 BrowserView 的窗口,未附着到任何窗口时为 null。注意:BrowserView 类已弃用,由 WebContentsView 取代 |
源码实现上,browser-window.ts 中 fromId 与 getAllWindows 复用 BaseWindow 的对应静态方法,再按 constructor.name === 'BrowserWindow' 过滤;fromWebContents 则直接调用 webContents.getOwnerBrowserWindow()。
实例属性
由 new BrowserWindow 创建的对象具有以下属性(JS 层属性多与对应方法成对出现,见 base-window.ts):
| 属性 | 说明 |
|---|---|
win.webContents(只读) |
窗口拥有的 WebContents 对象,所有与网页相关的事件和操作都通过它进行 |
win.id(只读) |
窗口唯一整型 ID,在整个 Electron 应用的所有 BrowserWindow 实例间唯一。源码在构造时即固化该值(Object.defineProperty 设为不可写),即使底层窗口销毁后仍可访问 |
win.tabbingIdentifier(macOS,只读) |
构造时传入的标签组标识,未设置时为 undefined |
win.autoHideMenuBar(Linux/Windows) |
菜单栏是否自动隐藏;设置后按单个 Alt 键才会显示。若菜单栏已可见,设为 true 不会立即隐藏 |
win.simpleFullScreen |
是否处于 simple(pre-Lion)全屏模式 |
win.fullScreen |
是否处于全屏模式 |
win.focusable(Windows/macOS) |
窗口是否可聚焦 |
win.visibleOnAllWorkspaces(macOS/Linux) |
是否在所有工作区可见;Windows 上恒为 false |
win.shadow |
窗口是否有阴影 |
win.menuBarVisible(Windows/Linux) |
菜单栏是否可见。自动隐藏时用户仍可按 Alt 调出 |
win.kiosk |
是否处于 kiosk 模式 |
win.documentEdited(macOS) |
窗口文档是否被编辑过;为 true 时标题栏图标变灰 |
win.representedFilename(macOS) |
窗口所表示文件的路径,对应文件图标显示在标题栏 |
win.title |
原生窗口标题。注意网页标题可能与原生窗口标题不同 |
win.minimizable / win.maximizable(macOS/Windows) |
是否可手动最小化/最大化。Linux 上 setter 是空操作,getter 返回 true |
win.fullScreenable |
最大化/缩放按钮是切换全屏还是最大化 |
win.resizable |
是否可手动调整大小 |
win.closable(macOS/Windows) |
是否可手动关闭。Linux 上 setter 是空操作,getter 返回 true |
win.movable(macOS/Windows) |
是否可被用户移动。Linux 上 setter 是空操作,getter 返回 true |
win.excludedFromShownWindowsMenu(macOS) |
是否从应用 Windows 菜单中排除,默认 false |
win.accessibleTitle |
仅供辅助功能工具(如读屏器)的替代标题,用户不可见 |
win.snapped(Windows,只读) |
窗口是否通过 Snap(窗口贴靠)布局 |
excludedFromShownWindowsMenu 的用法示例(原文档标注了 @ts-expect-error 说明当前类型定义未覆盖该属性):
const win = new BrowserWindow({ height: 600, width: 600 })
const template = [
{
role: 'windowmenu'
}
]
win.excludedFromShownWindowsMenu = true
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
实例方法
生命周期
win.destroy():强制关闭窗口。网页的 unload 与 beforeunload 事件、窗口的 close 事件都不会触发,但保证 closed 事件会触发。
win.close():尝试关闭窗口,效果等同于用户手动点击关闭按钮,网页可以取消(见 close 事件)。
聚焦与显示
| 方法 | 说明 |
|---|---|
win.focus() |
聚焦窗口。Wayland(Linux)上若窗口/应用未聚焦,桌面环境可能弹出通知或闪烁应用图标 |
win.blur() |
移除窗口焦点。Wayland(Linux)不支持 |
win.isFocused() |
返回 boolean,窗口是否聚焦 |
win.isDestroyed() |
返回 boolean,窗口是否已销毁 |
win.show() |
显示并聚焦窗口 |
win.showInactive() |
显示窗口但不聚焦。Wayland(Linux)不支持 |
win.hide() |
隐藏窗口 |
win.isVisible() |
返回 boolean,窗口是否在前台对用户可见 |
win.isModal() |
返回 boolean,当前窗口是否为模态窗口 |
最大化 / 最小化 / 全屏
| 方法 | 说明 |
|---|---|
win.maximize() / win.unmaximize() / win.isMaximized() |
最大化/取消最大化/查询;maximize() 若窗口未显示还会显示(但不聚焦)窗口 |
win.minimize() / win.restore() / win.isMinimized() |
最小化/恢复/查询;某些平台上最小化窗口会显示在 Dock |
win.setFullScreen(flag) |
设置是否全屏。注意:macOS 上全屏切换是异步的,依赖全屏状态的操作应监听 enter-full-screen/leave-full-screen 事件 |
win.isFullScreen() |
查询是否全屏。macOS 上同理,应确认全屏事件已发出后再查询 |
win.setSimpleFullScreen(flag)(macOS) |
进入/退出 simple 全屏模式(模拟 macOS Lion 10.7 之前的原生全屏行为) |
win.isSimpleFullScreen()(macOS) |
查询是否 simple 全屏 |
win.isNormal() |
是否处于正常状态(未最大化、未最小化、未全屏) |
宽高比与背景色
win.setAspectRatio(aspectRatio[, extraSize])
aspectRatioFloat——要维持的内容区宽高比extraSizeSize(可选,macOS)——不参与宽高比计算的额外尺寸
该方法让窗口保持指定宽高比。extraSize 允许开发者指定若干像素空间不计入宽高比计算(API 已考虑窗口尺寸与内容尺寸的差异)。典型场景:一个带 HD 视频播放器与控制条的窗口,左侧 15 像素控制区、右侧 25 像素控制区、下方 50 像素控制区。为在播放器内部维持 16:9(HD 1920x1080 标准比例),调用 setAspectRatio(16/9, { width: 40, height: 50 })。第二个参数不关心额外宽高在内容区中的位置,只需累计内容区中所有额外宽高的总和。
宽高比在通过 win.setSize 等 API 编程调整窗口时不受尊重。重置宽高比传入 0:win.setAspectRatio(0)。
win.setBackgroundColor(backgroundColor)
backgroundColorstring——Hex、RGB、RGBA、HSL、HSLA 或命名 CSS 颜色格式;hex 类型的 alpha 通道可选。
合法取值示例:
- Hex:
#fff(简写 RGB)、#ffff(简写 ARGB)、#ffffff(RGB)、#ffffffff(ARGB) - RGB:
rgb(([\d]+),\s*([\d]+),\s*([\d]+)),如rgb(255, 255, 255) - RGBA:如
rgba(255, 255, 255, 1.0) - HSL:如
hsl(200, 20%, 50%) - HSLA:如
hsla(200, 20%, 50%, 0.5) - 颜色名:与 CSS Color Module Level 3 关键字类似但大小写敏感,如
blueviolet、red
配合 优雅显示窗口 一节使用。
win.getBackgroundColor():返回 string,窗口背景色,Hex(#RRGGBB)格式。注意 alpha 值不会与 RGB 值一起返回。
边界与尺寸
win.setBounds(bounds[, animate])
boundsPartial<Rectangle>;animateboolean(可选,macOS)- 调整并移动窗口到给定边界,未提供的属性保持当前值。Wayland(Linux)上限制与
setSize/setPosition相同。
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
// set all bounds properties
win.setBounds({ x: 440, y: 225, width: 800, height: 600 })
// set a single bounds property
win.setBounds({ width: 100 })
// { x: 440, y: 225, width: 100, height: 600 }
console.log(win.getBounds())
注意:macOS 上 y 坐标不能小于 Tray 的高度(随系统版本在 20~40px 之间变化)。传入低于 tray 高度的值会得到紧贴 tray 的窗口。
源码印证:browser-window.ts 中 JS 层重写了 setBounds,先与 getBounds() 的当前值合并再调用原生实现——这正是文档“未提供的属性保持当前值”语义的实现位置,也是 win.setBounds({ width: 100 }) 只改宽度的原因。
win.getBounds():返回 Rectangle。macOS 上返回的 y 坐标最小为 tray 高度(例:tray 高 38 时,setBounds({ x: 25, y: 20, ... }) 后 getBounds() 返回 { x: 25, y: 38, ... });Wayland 上因禁止内省/编程修改全局窗口坐标,返回 { x: 0, y: 0, ... }。
win.setContentBounds(bounds[, animate]) / win.getContentBounds():调整/查询窗口客户区(即网页区域)的边界;Wayland 限制同上。
win.getNormalBounds():返回正常状态下的窗口边界 Rectangle。无论窗口当前处于何种状态(最大化、最小化、全屏),始终返回正常状态的坐标尺寸;正常状态下 getBounds 与 getNormalBounds 返回相同值。
win.setEnabled(enable) / win.isEnabled():禁用/启用窗口及查询。
win.setSize(width, height[, animate]) / win.getSize():调整/查询窗口宽高;低于已设最小约束时窗口会“吸附”到最小尺寸。Wayland 上可能失效(部分窗口管理器限制编程调尺寸)。
win.setContentSize(width, height[, animate]) / win.getContentSize():调整/查询客户区宽高;Wayland 同上。
win.setMinimumSize(width, height) / win.getMinimumSize();win.setMaximumSize(width, height) / win.getMaximumSize():设置/查询最小、最大宽高(Integer[] 返回)。
窗口能力开关
| 方法对 | 平台 | 说明 |
|---|---|---|
win.setResizable(resizable) / win.isResizable() |
全平台 | 用户能否手动调整大小 |
win.setMovable(movable) / win.isMovable() |
macOS/Windows | 用户能否移动窗口;Linux 上 setter 无效,getter 恒 true |
win.setMinimizable(minimizable) / win.isMinimizable() |
macOS/Windows | 能否最小化;Linux 上 setter 无效,getter 恒 true |
win.setMaximizable(maximizable) / win.isMaximizable() |
macOS/Windows | 能否最大化;Linux 上 setter 无效,getter 恒 true |
win.setFullScreenable(fullscreenable) / win.isFullScreenable() |
全平台 | 最大化/缩放按钮是切换全屏还是最大化 |
win.setClosable(closable) / win.isClosable() |
macOS/Windows | 能否关闭;Linux 上 setter 无效,getter 恒 true |
win.setFocusable(focusable) / win.isFocusable() |
macOS/Windows | 窗口能否被聚焦;macOS 上不会移除当前焦点 |
win.isHiddenInMissionControl() / win.setHiddenInMissionControl(hidden) |
macOS | 切换 Mission Control 时窗口是否隐藏 |
win.setKiosk(flag) / win.isKiosk() |
全平台 | 进入/退出 kiosk 模式及查询 |
Z 序与位置
| 方法 | 说明 |
|---|---|
win.setAlwaysOnTop(flag[, level][, relativeLevel]) |
是否始终置顶。level(macOS/Windows)可选值:normal、floating、torn-off-menu、modal-panel、main-menu、status、pop-up-menu、screen-saver、dock(已弃用);flag 为 true 时默认 floating,为 false 时 level 重置为 normal。floating 到 status 之间窗口位于 Dock(macOS)/任务栏(Windows)之下,pop-up-menu 及以上则在其上。relativeLevel(macOS)相对给定 level 再高若干层,默认 0,Apple 不建议高于 screen-saver 以上 1 层。设置后窗口仍是普通窗口而非无法聚焦的 toolbox 窗口。Wayland(Linux)不支持 |
win.isAlwaysOnTop() |
是否始终置顶;Wayland 不支持 |
win.moveAbove(mediaSourceId) |
将窗口移到 mediaSourceId(DesktopCapturerSource id,格式如 "window:1869:0")对应窗口的 z 序之上;id 类型不是 window 或窗口不存在时抛错 |
win.moveTop() |
无论焦点如何,将窗口移到 z 序顶部;Wayland 不支持 |
win.center() |
将窗口移到屏幕中央;Wayland 不支持 |
win.setPosition(x, y[, animate]) |
移动窗口到 x、y;Wayland 不支持 |
win.getPosition() |
返回 Integer[] 当前位置;Wayland 上返回 [0, 0](禁止内省全局坐标) |
标题、菜单与内容加载
win.setTitle(title) / win.getTitle():设置/查询原生窗口标题。网页标题与原生窗口标题可以不同。
win.setMenu(menu)(Linux/Windows):设置窗口菜单栏,menu 为 Menu | null。win.removeMenu()(Linux/Windows):移除菜单栏。
win.setAutoHideMenuBar(hide) / win.isMenuBarAutoHide()(Windows/Linux):菜单栏是否自动隐藏,设置后按单个 Alt 键才会显示;已可见时调用 setAutoHideMenuBar(true) 不会立即隐藏。
win.setMenuBarVisibility(visible) / win.isMenuBarVisible()(Windows/Linux):菜单栏是否可见。
win.loadURL(url[, options])
urlstringoptionsObject(可选):httpReferrer(string | Referrer)(可选)——HTTP Referrer URLuserAgentstring(可选)——发起请求的 User AgentextraHeadersstring(可选)——以 "\n" 分隔的附加请求头postData(UploadRawData | UploadFile)[](可选)baseURLForDataURLstring(可选)——data URL 中加载其他文件所需的基址 URL(须带尾部路径分隔符)
返回 Promise<void>,页面加载完成(见 did-finish-load)时 resolve,加载失败(见 did-fail-load)时 reject;已内置 noop rejection handler,避免未处理拒绝错误。若现有页面有 beforeUnload handler,未处理 will-prevent-unload 时 did-fail-load 会被调用。等价于 webContents.loadURL(url[, options])。
url 可以是远程地址(如 http://),也可以是 file:// 协议的本地 HTML 文件路径。为规范化 file URL,推荐使用 Node 的 url.format:
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
const url = require('node:url').format({
protocol: 'file',
slashes: true,
pathname: require('node:path').join(__dirname, 'index.html')
})
win.loadURL(url)
也可以发送 POST 请求(URL 编码数据)加载 URL:
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
win.loadURL('http://localhost:8000/post', {
postData: [{
type: 'rawData',
bytes: Buffer.from('hello=world')
}],
extraHeaders: 'Content-Type: application/x-www-form-urlencoded'
})
win.loadFile(filePath[, options])
filePathstring;options可选:queryRecord<string, string>、searchstring、hashstring(均传给url.format())
返回 Promise<void>,resolve/reject 语义同 loadURL。filePath 应为相对应用根目录的 HTML 文件路径,细节见 webContents.loadFile 文档。
win.reload():同 webContents.reload。
平台专属:macOS
| 方法 | 说明 |
|---|---|
win.previewFile(path[, displayName]) |
用 Quick Look 预览文件。path 必须是绝对路径(Quick Look 用文件名与扩展名判断内容类型);displayName 仅视觉用途,默认 path |
win.closeFilePreview() |
关闭当前 Quick Look 面板 |
win.setSheetOffset(offsetY[, offsetX]) |
修改 sheet 附着点。默认附着在窗口框正下方,若要在 HTML 渲染的工具栏下方显示可:const toolbarRect = document.getElementById('toolbar').getBoundingClientRect(); win.setSheetOffset(toolbarRect.height) |
win.setRepresentedFilename(filename) / win.getRepresentedFilename() |
设置/查询窗口所表示文件的路径,文件图标显示在标题栏 |
win.setDocumentEdited(edited) / win.isDocumentEdited() |
窗口文档是否已编辑;true 时标题栏图标变灰 |
win.invalidateShadow() |
使窗口阴影失效并按当前窗口形状重算。透明 BrowserWindow 在 macOS 上可能留下视觉残留,动画等场景可调用此方法清除 |
win.showDefinitionForSelection() |
同 webContents.showDefinitionForSelection()(为选中文字显示词典定义) |
win.setWindowButtonVisibility(visible) |
红绿灯按钮是否可见 |
win.setWindowButtonPosition(position) / win.getWindowButtonPosition() |
设置/查询无边框窗口红绿灯按钮的自定义位置(Point),传 null 重置为默认 |
win.setAutoHideCursor(autoHide) |
输入时是否隐藏光标 |
win.setVibrancy(type[, options]) |
添加毛玻璃效果。type 可为 titlebar、selection、menu、popover、sidebar、header、sheet、window、hud、fullscreen-ui、tooltip、content、under-window、under-page 或 null/空串(移除效果)。options.animationDuration(毫秒)> 0 时对淡入/淡出做动画;类型之间切换不支持动画 |
win.setTouchBar(touchBar) |
设置当前窗口的 TouchBar 布局,null/undefined 清除;仅在机器有 TouchBar 时生效。TouchBar API 目前为实验性 |
macOS 原生标签页(需启用 native tabs):win.selectPreviousTab() / win.selectNextTab()(选择上一个/下一个标签)、win.showAllTabs()(显示/隐藏标签概览)、win.mergeAllWindows()(把所有窗口合并为一个多标签窗口)、win.moveTabToNewWindow()(当前标签移到新窗口)、win.toggleTabBar()(单标签窗口切换标签栏可见性)、win.addTabbedWindow(browserWindow)(把一个窗口作为标签添加到本窗口,位于本窗口标签之后)。
平台专属:Windows
| 方法 | 说明 |
|---|---|
win.setProgressBar(progress[, options]) |
设置进度条进度,有效范围 [0, 1.0];progress < 0 移除进度条;progress > 1 变为不确定模式。options.mode 可取 none、normal、indeterminate、error、paused;不传 mode(值在有效范围内)时默认 normal。Linux 上进度条显示在支持 LauncherEntry D-Bus API 的 dock/任务栏,与应用 .desktop 文件关联,需保证 app.setDesktopName(见 app 文档)与实际 .desktop 文件名一致,且 Linux 不支持不确定模式 |
win.setOverlayIcon(overlay, description) |
在任务栏图标右下角设置 16x16 覆盖图标(用于传达应用状态或被动通知);overlay 为 NativeImage 或 null(清除覆盖);description 提供给读屏器 |
win.isTabletMode() |
是否处于 Windows 10 平板模式;可在该模式下为平板优化 UI(如放大标题栏、隐藏标题栏按钮)。窗口处于平板模式与否可用本方法查询,resize 事件可监听模式变化 |
win.getNativeWindowHandle() |
返回 Buffer,平台特定窗口句柄:Windows 为 HWND,macOS 为 NSView*,Linux 为 Window(unsigned long) |
win.hookWindowMessage(message, callback) |
钩住窗口消息,消息到达 WndProc 时调用 callback(wParam: Buffer, lParam: Buffer) |
win.isWindowMessageHooked(message) / win.unhookWindowMessage(message) / win.unhookAllWindowMessages() |
查询/取消单个消息钩子/取消全部消息钩子 |
win.setThumbarButtons(buttons) |
为任务栏缩略图添加缩略工具栏按钮,返回 boolean 表示是否添加成功。按钮数不得超过 7;工具栏一经设置无法移除(平台限制),可传空数组清空按钮。按钮对象:icon(NativeImage)、click(Function)、tooltip(可选)、flags(可选字符串数组,默认 ['enabled'])。flags 取值:enabled(可用)、disabled(禁用)、dismissonclick(点击后缩略窗口立即关闭)、nobackground(不画按钮边框只用图片)、hidden(不显示)、noninteractive(可用但不可交互,不绘制按下状态) |
win.setThumbnailClip(region) |
设置鼠标悬停任务栏时缩略图展示的窗口区域(Rectangle);传空区域 { x: 0, y: 0, width: 0, height: 0 } 重置为整个窗口 |
win.setThumbnailToolTip(toolTip) |
设置任务栏缩略图悬停提示 |
win.setAppDetails(options) |
设置任务栏按钮属性:appId(App User Model ID,必须设置否则其余选项无效)、appIconPath(重启图标路径)、appIconIndex(图标索引,默认 0)、relaunchCommand(重启命令)、relaunchDisplayName(重启显示名)。注意:relaunchCommand 与 relaunchDisplayName 必须成对设置,缺一则两者都不生效 |
win.setAccentColor(accentColor) |
设置窗口系统强调色与激活边框高亮。accentColor 可为:颜色字符串(CSS 标准格式,Alpha 被忽略,按完全不透明处理)、true(用系统强调色启用高亮,无视系统设置)、false(禁用高亮)、null(重置为跟随系统设置) |
win.getAccentColor() |
返回 string | boolean。若窗口设置了与系统不同的强调色,返回 Hex RGB 颜色字符串;否则返回布尔值——true 表示使用全局系统强调色,false 表示该窗口禁用了强调色高亮 |
win.isSnapped() |
窗口是否通过 Snap 布局(悬停最大化按钮出现的按钮,或拖拽到屏幕边缘) |
win.setIcon(icon)(Windows/Linux) |
更改窗口图标,icon 为 NativeImage 或 string |
win.setSkipTaskbar(skip)(macOS/Windows) |
窗口是否不在任务栏显示 |
win.setBackgroundMaterial(material) |
设置系统绘制背景材质(含非客户区之后)。取值:auto(DWM 自动决定,默认)、none(不画)、mica(长生命周期窗口材质)、acrylic(瞬态窗口材质)、tabbed(带标签标题栏的窗口材质)。仅 Windows 11 22H2 及以上支持 |
win.setTitleBarOverlay(options) |
更新已启用 Window Controls Overlay 窗口的样式:color(Overlay CSS 颜色)、symbolColor(符号 CSS 颜色)、height(标题栏与 Overlay 高度,像素)。Linux 上未显式设置 symbolColor 时会自动计算与 color 具备最小可访问对比度的颜色 |
win.isContentProtected() / win.setContentProtection(enable)(macOS/Windows) |
防止窗口内容被其他应用捕获。Windows 上以 WDA_EXCLUDEFROMCAPTURE 调用 SetWindowDisplayAffinity:Win10 2004+ 窗口完全从捕获中移除,旧版本表现为黑窗;macOS 上将 NSWindow 的 sharingType 设为 NSWindowSharingNone,但使用 ScreenCaptureKit 的新版 macOS 应用仍可捕获窗口(平台行为变更) |
其他方法
win.capturePage([rect, opts])
rectRectangle(可选)——要捕获的边界optsObject(可选):stayHiddenboolean(保持页面隐藏而非可见,默认false);stayAwakeboolean(保持系统不进入睡眠,默认false)
返回 Promise<NativeImage>。捕获 rect 内的页面快照,省略 rect 则捕获整个可见页面;页面不可见时 rect 可能为空。页面在浏览器窗口隐藏且捕获计数非零时被视为可见;希望页面保持隐藏时应确保 stayHidden 为 true。
win.focusOnWebView() / win.blurWebView():聚焦/取消聚焦网页视图(而非整个窗口)。
win.flashFrame(flag):开始/停止闪烁窗口以吸引用户注意;macOS 上会持续闪烁 dock 图标。
win.setOpacity(opacity) / win.getOpacity():设置/查询窗口不透明度,取值 0.0(全透明)~ 1.0(全不透明),越界值会被钳制到 [0, 1]。
win.setShape(rects)(Windows/Linux,实验性):rects 为 Rectangle[],为窗口设置形状;传空列表恢复矩形窗口。形状决定了系统允许绘制和用户交互的区域——区域外不绘制像素、不接收鼠标事件,区域外的鼠标事件会穿透到窗口背后的内容。
win.setHasShadow(hasShadow) / win.hasShadow():设置/查询窗口是否有阴影。
win.setVisibleOnAllWorkspaces(visible[, options])(macOS/Linux):窗口是否在所有工作区可见。options.visibleOnFullScreen(macOS)设置是否可见于全屏窗口之上;options.skipTransformProcessType(macOS)——该调用默认会在 UIElementApplication 与 ForegroundApplication 之间转换进程类型,导致窗口和 dock 短暂隐藏;若窗口已是 UIElementApplication 类型,传 true 可跳过转换。Windows 上该 API 无效(isVisibleOnAllWorkspaces() 恒返回 false)。
win.setIgnoreMouseEvents(ignore[, options]):让窗口忽略所有鼠标事件。窗口内发生的鼠标事件全部传递给下方窗口;但窗口持有焦点时仍接收键盘事件。options.forward(macOS/Windows)为 true 且 ignore 为 true 时,会把 mouse move 消息转发给 Chromium,使 mouseleave 等鼠标相关事件可用;ignore 为 false 时转发总是关闭。
win.setParentWindow(parent) / win.getParentWindow() / win.getChildWindows():设置父窗口(null 恢复顶层)/查询父窗口/获取全部子窗口。
win.getMediaSourceId():返回窗口在 DesktopCapturerSource 格式下的 id,如 "window:1324:0"。更精确地说是 window:id:other_id,其中 id 在 Windows 是 HWND、macOS 是 CGWindowID(uint64_t)、Linux 是 Window(unsigned long);other_id 用于标识同一顶层窗口内的 web contents(标签)。
已弃用:BrowserView 相关
以下方法已标记为 Experimental / Deprecated——BrowserView 类已弃用,由 WebContentsView 类取代,新代码不应再使用:
win.setBrowserView(browserView):附着browserView到窗口,其他已附着的 BrowserView 会被移除win.getBrowserView():返回附着的 BrowserView,多个时抛错win.addBrowserView(browserView):支持多 BrowserView 的替代 APIwin.removeBrowserView(browserView):移除 BrowserViewwin.setTopBrowserView(browserView):将某 BrowserView 提升到最上层(未附着时抛错)win.getBrowserViews():返回按 z 序排序的 BrowserView 数组,最上层为最后一项
小结
BrowserWindow 以 BaseWindow 的通用窗口能力(边界、状态事件、父子关系)为基础,叠加了 webContents 驱动的页面加载、ready-to-show 首帧控制与丰富的平台专属外观 API。掌握本文的核心要点:用 ready-to-show + backgroundColor 消除显示闪烁;用 paintWhenInitiallyHidden 让 show: false 场景下的可见性语义正确;理解 will-resize/will-move 只在用户手动操作时触发;以及按平台裁剪 Windows 任务栏、macOS sheet/标签页/毛玻璃等专属能力——即可在 Electron 中构建行为可预期、体验接近原生的跨平台桌面窗口。完整的 API 参考以仓库中 browser-window.md、base-window-options.md 与 browser-window-options.md 为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
