在 Electron 里,每一个应用窗口都对应一个 BrowserWindow 实例。创建窗口的过程十分简单,但真正需要你熟悉的,是这一大串配置项如何在不同的业务场景下做到“恰到好处”。这一节会聚焦于最常用的四个配置:尺寸、位置、标题和图标,并给出实战中经常遇到的小陷阱。
基础用法
一个典型的窗口创建代码如下:
const { app, BrowserWindow } = require('electron');
const path = require('path');
app.whenReady().then(() => {
const mainWindow = new BrowserWindow({
// 尺寸
width: 1200,
height: 800,
// 位置(左上角坐标)
x: 100,
y: 100,
// 标题
title: '我的应用',
// 图标
icon: path.join(__dirname, 'assets/icon.png'),
// 其余常用配置
show: false, // 等窗口内容加载完再显示,避免白屏
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
mainWindow.loadFile('index.html');
// 页面渲染完成后再优雅地显示窗口
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
});
下面逐一讲解四个核心配置项的作用和常见用法。
1. 尺寸:width 与 height
- 单位:像素。
- 默认值:如果不填,Electron 会使用一个默认尺寸(通常是 800×600)。
- 最小/最大限制:你还可以配合
minWidth、maxWidth、minHeight、maxHeight来锁定窗口可调整的范围。例如,一个固定大小的设置弹窗通常会写成:
width: 400,
height: 300,
resizable: false // 禁止拖拽调整大小
- 启动时记住上一次的窗口大小:这是桌面应用的常见体验。很多人会手动调用
window.getBounds()然后在下次启动时传入。但推荐的方式是使用社区库electron-store或electron-window-state自动处理窗口状态的持久化,无需自己从零实现。
实战提醒:尽量避免在代码中写死绝对的像素值。如果你的应用支持高分屏,用户将系统缩放改成 125% 或 150% 时,窗口内容会自动缩放(因为 Chromium 正确处理了 DPI)。但如果你使用了 maxWidth 等限制,要确保这些限制在高 DPI 下依然合理,否则窗口可能被挤成一团。
2. 位置:x 和 y
- 指定窗口左上角在屏幕上的坐标(以屏幕左上角为原点)。
- 如果不设置,操作系统会自动选择一个合适的位置(通常是屏幕中央偏上的位置,由窗口管理器决定)。
- 如果你希望窗口始终居中,可以使用
center: true,它会忽略x和y的配置。 - 多显示器场景:
center: true只会以主显示器居中。更精细的控制可以用screen模块获取所有显示器的边界,然后针对当前拥有鼠标焦点的显示器居中,代码大致如下:
const { screen } = require('electron');
const primaryDisplay = screen.getPrimaryDisplay();
const { width, height } = primaryDisplay.workAreaSize;
// 自定义计算 x, y
真实案例:很多团队为了兼容用户将窗口拖到副屏后关闭再打开的需求,会在关闭时保存 bounds(通过 win.getBounds()),启动时再恢复,这样窗口就会出现在用户上次停留的位置。
3. 标题:title
- 显示在窗口标题栏的字符串。
- 它会自动覆盖 HTML
<title>标签的内容。也就是说,如果index.html里写了<title>旧标题</title>,只要你创建窗口时指定了title: '新标题',最终显示的就是“新标题”。 - 如果想动态更新标题,可以在稍后任意时间调用
win.setTitle('新的标题')。 - macOS 上的特殊行为:macOS 程序通常会把自己的名字放在菜单栏而不是每个窗口的标题栏上,因此标题栏的文字看起来可能不那么显眼。如果你想在 macOS 上也保留窗口标题,无需额外操作,它就在那里。
注意:title 是一个纯字符串,不支持 HTML 标签或特殊格式。如果你需要更灵活的标题样式(比如某些应用会在标题中加入未读消息数),可以使用无边框窗口(frame: false)然后自己用 HTML/CSS 绘制标题栏区域,完全绕过原生标题栏的限制。
4. 图标:icon
这是新手最容易踩坑的地方,因为不同操作系统对图标格式的要求完全不一样。
- Windows:需要
.ico文件(推荐包含多个尺寸,如 16×16、32×32、48×48、256×256)。虽然也可以使用.png,但当固定到任务栏或者出现在系统对话框中时,可能会出现缩放模糊。专业的做法是一张icon.ico包含多分辨率。 - macOS:需要
.icns格式,或者在打包时由构建工具自动从icon.png(至少 512×512)生成。开发过程中,指定一个.png也能在菜单栏和 Dock 中看到效果,但正式发布一定要用.icns。 - Linux:通常是
.png,但各个桌面环境可能略有差异。打包时通过.desktop文件指定图标路径。 - 开发路径:在代码中使用相对路径时,务必用
path.join(dirname, '...')确保路径正确,因为dirname是主进程脚本文件的目录。
一个稳妥的文件结构:
my-app/
├── main.js
├── assets/
│ ├── icon.ico # Windows
│ ├── icon.icns # macOS
│ ├── icon.png # Linux / 开发通用
然后根据平台动态选择图标:
const iconPath = process.platform === 'win32'
? 'assets/icon.ico'
: 'assets/icon.png';
const mainWindow = new BrowserWindow({
icon: path.join(__dirname, iconPath),
// ...
});
打包后的图标:BrowserWindow 的 icon 仅在开发时或窗口运行时生效。应用安装后,桌面快捷方式、任务栏应用图标、安装包自身的图标,需要在 electron-builder 的配置中单独指定(例如 build/icon.icns),这两个概念是分离的。
一个组合实战:创建带记忆功能的主窗口
下面是一段直接从真实项目抽出来的代码,它使用了 electron-window-state 来记住窗口大小和位置,同时配置了标题和图标的跨平台处理:
const { app, BrowserWindow } = require('electron');
const windowStateKeeper = require('electron-window-state');
const path = require('path');
function createMainWindow() {
// 自动加载上次关闭时的窗口状态
const mainWindowState = windowStateKeeper({
defaultWidth: 1200,
defaultHeight: 800,
});
const win = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
title: 'Notes Pro',
icon: path.join(__dirname, 'assets/icon.png'), // 开发用
show: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
// 绑定状态管理器,自动在移动/调整大小时更新
mainWindowState.manage(win);
win.loadFile('dist/index.html');
win.once('ready-to-show', () => {
win.show();
});
return win;
}
这样,用户调整窗口后关闭再重新打开,窗口会直接恢复到之前的大小和位置,体验非常自然。
扩展思考:除了这四个,还有哪些配置值得优先关注?
在《窗口创建与配置项》这一节里,虽然只重点讨论了尺寸、位置、标题和图标,但真实开发中你几乎马上就会遇到这些高频配置:
frame: false:无边框窗口,用于自定义标题栏。transparent: true:配合frame: false做圆角窗口或异形窗口。alwaysOnTop: true:窗口置顶,工具类窗口常用。resizable: false:固定大小,适合对话框。backgroundColor: '#2e2c29':减少白屏,设置加载时的背景色。
这些细节将在后续章节结合具体场景深入展开。
掌握了 Windows 创建与基础配置,你就能搭建出一个看起来、用起来都像样的桌面程序了。下一节,我们将深入窗口的生命周期管理,了解如何优雅地关闭、隐藏、销毁窗口,以及在 macOS 和 Windows 上截然不同的窗口行为该如何处理。