在 Electron 中,BrowserWindow 是你打交道最多的一个类。每个应用程序窗口都由它创建,通过它可以控制窗口的大小、位置、外观、行为,以及加载什么内容。这一节会从最基本的用法开始,带你熟悉 BrowserWindow 的核心能力。
7.2.1 创建第一个窗口
最简单的创建方式只需要两个参数:宽度和高度。下面是在主进程 main.js 中通常见到的代码:
const { app, BrowserWindow } = require('electron');
app.whenReady().then(() => {
const win = new BrowserWindow({
width: 1000,
height: 700,
});
win.loadFile('index.html');
});
这四行代码已经生成了一个标准的桌面窗口,里面有完整的标题栏、可拖拽调节大小、系统关闭按钮。loadFile 方法加载了项目中的一个本地 HTML 文件。
如果想加载一个远程 URL,比如你的网站,把 loadFile 换成 loadURL 即可:
win.loadURL('https://example.com');
7.2.2 常用的配置选项
真实项目里,你很少只用到 width 和 height。BrowserWindow 的构造函数可以接收几十种选项,下面是一些马上能用到的:
| 选项 | 类型 | 说明 |
|------|------|------|
| width / height | Number | 窗口初始宽高,单位像素。 |
| x / y | Number | 窗口初始位置(相对于屏幕左上角)。不传则由系统自动居中。 |
| minWidth / minHeight | Number | 窗口最小尺寸,避免用户缩得太小导致界面错乱。 |
| maxWidth / maxHeight | Number | 窗口最大尺寸。 |
| resizable | Boolean | 是否允许用户拖拽边框调整大小,默认 true。 |
| movable | Boolean | 是否允许用户拖拽标题栏移动窗口,默认 true。 |
| frame | Boolean | 是否显示标题栏和边框,默认 true。设为 false 可创建无边框窗口(常用来做自定义标题栏)。 |
| transparent | Boolean | 是否允许窗口透明,默认 false。需搭配 CSS 背景透明和 frame: false 使用。 |
| alwaysOnTop | Boolean | 是否始终置顶于其他窗口之上,默认 false。 |
| icon | String | 窗口图标路径(Windows 显示在任务栏和标题栏,macOS 显示在程序坞)。 |
| show | Boolean | 创建后是否立即显示,默认 true。设为 false 配合 ready-to-show 事件可实现先加载完再显示,避免白屏。 |
一个更贴近实际使用的初始化配置如下:
const win = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
icon: path.join(__dirname, 'assets/icon.png'),
show: false, // 先不显示,等页面渲染完再出现
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false, // 出于安全关闭 Node 集成
contextIsolation: true, // 启用上下文隔离
},
});
7.2.3 窗口的生命周期事件
窗口从创建到关闭有一系列事件,处理它们可以让你精确控制行为。以下是开发中最常用的几个:
// 页面内容渲染完成,适合隐藏 loading 动画并显示窗口
win.once('ready-to-show', () => {
win.show();
// 如果设置了 show: false,在这里让窗口可见
});
// 窗口获得焦点时触发,适合恢复某些状态
win.on('focus', () => {
console.log('窗口获得焦点');
});
// 窗口失去焦点时触发,适合暂停动画、保存草稿等
win.on('blur', () => {
console.log('窗口失去焦点');
});
// 窗口关闭时触发。通常用它来隐藏窗口到托盘而不是退出程序
win.on('close', (event) => {
// 可以阻止默认关闭行为,改为隐藏到托盘
// event.preventDefault();
// win.hide();
});
// 窗口已经销毁,可以在这里进行清理操作
win.on('closed', () => {
// 手动解除引用
// win = null;
});
最常用的组合是 ready-to-show + show: false,它能大幅度改善用户体验:用户打开应用时见到的是一个已经渲染完的完整界面,而不是短暂的白屏或闪烁。
7.2.4 加载内容与切换页面
除了 loadFile 和 loadURL,BrowserWindow 还提供了一些辅助方法:
win.loadURL(url, options):加载远程或本地 HTTP 地址。win.loadFile(filePath):加载本地 HTML 文件,路径使用相对路径即可,Electron 会自动处理。win.webContents.loadURL():与win.loadURL等效,可以附加更多选项,如 HTTP Referrer 等。
如果需要在同一个窗口内切换到不同的页面,直接再次调用加载方法就行:
// 从主页切换到关于页面
win.loadFile('about.html');
对于更复杂的单页应用(如 Vue Router、React Router),你在渲染进程中管理路由即可,窗口本身只需载入一次入口 HTML。
7.2.5 子窗口与父子关系
Electron 允许你创建子窗口,它会受到父窗口的约束(比如父窗口最小化时它跟着最小化):
// 创建一个从属于主窗口的子窗口
const childWin = new BrowserWindow({
width: 600,
height: 400,
parent: win, // 指定父窗口
modal: true, // 设为模态,阻止用户操作父窗口
show: false,
});
childWin.loadFile('settings.html');
childWin.once('ready-to-show', () => {
childWin.show();
});
模态子窗口非常适合做设置面板、登录弹窗或确认对话框。它的行为更接近原生程序的“二级窗口”,而不是浏览器的弹出层。
7.2.6 无边框窗口与自定义标题栏
如果你想去掉系统自带的标题栏,完全用 HTML/CSS 实现窗口拖拽区和关闭按钮,只需要两步:
- 创建窗口时设置
frame: false。 - 在 HTML 中通过 CSS 属性
-webkit-app-region: drag指定拖拽区域。
.title-bar {
-webkit-app-region: drag; /* 允许拖拽 */
height: 40px;
background: #2c3e50;
}
.title-bar button {
-webkit-app-region: no-drag; /* 按钮点击不能被拖拽占用 */
}
<div class="title-bar">
<span>My App</span>
<button onclick="window.close()">X</button>
</div>
这种技术广泛应用于音乐播放器、代码编辑器等追求界面美观度一致的应用中。
7.2.7 保持窗口状态与记忆位置
用户通常期望下次打开应用时,窗口还在上一次关闭时的位置和大小。可以使用 electron-store 这类库持久化这些数据,在创建窗口时使用保存的值:
const Store = require('electron-store');
const store = new Store();
const winOptions = {
width: store.get('winWidth', 1000),
height: store.get('winHeight', 700),
x: store.get('winX'), // 如果未保存过,则为 undefined,由系统自行处理
y: store.get('winY'),
};
const win = new BrowserWindow(winOptions);
// 关闭时记录尺寸和位置
win.on('close', () => {
const [width, height] = win.getSize();
const [x, y] = win.getPosition();
store.set({ winWidth: width, winHeight: height, winX: x, winY: y });
});
用这种方式,你会给用户一种“这个应用感觉很专业”的顺畅体验。
掌握本节讲到的这些基础 API 和方法,你已经可以创建出完全符合桌面规范、能加载任意内容、与用户交互自然的应用窗口了。下一节我们会深入讲解不同平台的窗口差异处理,以及更高级的窗口控制技巧。