BrowserWindow窗口 #
一、BrowserWindow 概述 #
1.1 什么是 BrowserWindow #
BrowserWindow 是 Electron 中用于创建和管理应用窗口的核心模块。每个 BrowserWindow 实例都会创建一个独立的渲染进程。
text
┌─────────────────────────────────────────┐
│ BrowserWindow │
├─────────────────────────────────────────┤
│ ┌─────────────────────────────────┐ │
│ │ 标题栏 │ │
│ ├─────────────────────────────────┤ │
│ │ │ │
│ │ 渲染进程 │ │
│ │ (Web 页面) │ │
│ │ │ │
│ │ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
1.2 基本创建 #
javascript
const { BrowserWindow } = require('electron');
const win = new BrowserWindow({
width: 800,
height: 600
});
win.loadFile('index.html');
二、窗口配置选项 #
2.1 基础配置 #
javascript
const win = new BrowserWindow({
// 尺寸
width: 800,
height: 600,
minWidth: 400,
minHeight: 300,
maxWidth: 1200,
maxHeight: 900,
// 位置
x: 100,
y: 100,
center: true,
// 外观
title: '我的应用',
icon: '/path/to/icon.png',
backgroundColor: '#ffffff',
opacity: 1.0,
// 行为
resizable: true,
movable: true,
minimizable: true,
maximizable: true,
closable: true,
focusable: true,
alwaysOnTop: false,
// 状态
show: true,
fullscreen: false,
maximized: false,
minimized: false
});
2.2 Web 偏好配置 #
javascript
const win = new BrowserWindow({
webPreferences: {
// 安全配置
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
sandbox: true,
webSecurity: true,
allowRunningInsecureContent: false,
// 预加载脚本
preload: path.join(__dirname, 'preload.js'),
// 功能配置
javascript: true,
plugins: true,
experimentalFeatures: false,
// 缓存与存储
partition: 'persist:myapp',
webviewTag: false,
// 开发工具
devTools: true,
// 其他
spellcheck: true,
zoomFactor: 1.0
}
});
2.3 框架与透明 #
javascript
// 无边框窗口
const framelessWin = new BrowserWindow({
width: 800,
height: 600,
frame: false,
transparent: false
});
// 透明窗口
const transparentWin = new BrowserWindow({
width: 800,
height: 600,
frame: false,
transparent: true,
backgroundColor: '#00000000'
});
// macOS 特有配置
const macWin = new BrowserWindow({
width: 800,
height: 600,
titleBarStyle: 'hiddenInset', // hidden, hiddenInset, customButtonsOnHover
trafficLightPosition: { x: 10, y: 10 },
vibrancy: 'under-window' // appearance-based, light, dark, titlebar, selection, menu, popover, sidebar, medium-light, ultra-dark, header, sheet, window, hud, fullscreen-ui, tooltip, content, under-window, under-page
});
三、窗口方法 #
3.1 显示与隐藏 #
javascript
// 显示窗口
win.show();
win.showInactive(); // 显示但不获取焦点
// 隐藏窗口
win.hide();
// 关闭窗口
win.close(); // 尝试关闭,可能触发 close 事件
win.destroy(); // 强制关闭,不触发事件
// 最小化
win.minimize();
win.restore();
// 最大化
win.maximize();
win.unmaximize();
// 全屏
win.setFullScreen(true);
win.setSimpleFullScreen(true); // macOS 简单全屏
// 获取状态
win.isVisible();
win.isMinimized();
win.isMaximized();
win.isFullScreen();
3.2 尺寸与位置 #
javascript
// 获取尺寸
const [width, height] = win.getSize();
const [minWidth, minHeight] = win.getMinimumSize();
const [maxWidth, maxHeight] = win.getMaximumSize();
// 设置尺寸
win.setSize(1024, 768);
win.setMinimumSize(400, 300);
win.setMaximumSize(1920, 1080);
// 获取位置
const [x, y] = win.getPosition();
// 设置位置
win.setPosition(100, 100);
// 居中
win.center();
// 获取边界
const bounds = win.getBounds();
// { x: 100, y: 100, width: 800, height: 600 }
// 设置边界
win.setBounds({ x: 100, y: 100, width: 800, height: 600 });
// 内容尺寸(不含边框)
const [contentWidth, contentHeight] = win.getContentSize();
win.setContentSize(800, 600);
3.3 内容加载 #
javascript
// 加载本地文件
win.loadFile('index.html');
win.loadFile('pages/about.html');
// 加载 URL
win.loadURL('https://example.com');
// 加载 HTML 字符串
win.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`);
// 重新加载
win.reload();
win.reloadIgnoringCache();
// 加载状态
const url = win.webContents.getURL();
const isLoading = win.webContents.isLoading();
3.4 父子窗口 #
javascript
// 创建父窗口
const parentWin = new BrowserWindow({ width: 800, height: 600 });
// 创建子窗口
const childWin = new BrowserWindow({
width: 400,
height: 300,
parent: parentWin,
modal: true // 模态窗口
});
// 获取父窗口
const parent = childWin.getParentWindow();
// 获取所有子窗口
const children = parentWin.getChildWindows();
四、窗口事件 #
4.1 生命周期事件 #
javascript
// 窗口即将关闭
win.on('close', (event) => {
// 阻止关闭
event.preventDefault();
// 最小化到托盘而不是关闭
win.hide();
});
// 窗口已关闭
win.on('closed', () => {
// 清理引用
win = null;
});
// 窗口获得焦点
win.on('focus', () => {
console.log('窗口获得焦点');
});
// 窗口失去焦点
win.on('blur', () => {
console.log('窗口失去焦点');
});
// 窗口显示
win.on('show', () => {
console.log('窗口显示');
});
// 窗口隐藏
win.on('hide', () => {
console.log('窗口隐藏');
});
// 窗口最小化
win.on('minimize', () => {
console.log('窗口最小化');
});
// 窗口恢复
win.on('restore', () => {
console.log('窗口恢复');
});
// 窗口最大化
win.on('maximize', () => {
console.log('窗口最大化');
});
// 窗口取消最大化
win.on('unmaximize', () => {
console.log('窗口取消最大化');
});
// 进入全屏
win.on('enter-full-screen', () => {
console.log('进入全屏');
});
// 退出全屏
win.on('leave-full-screen', () => {
console.log('退出全屏');
});
4.2 页面加载事件 #
javascript
// 页面开始加载
win.webContents.on('did-start-loading', () => {
console.log('页面开始加载');
});
// 页面加载完成
win.webContents.on('did-finish-load', () => {
console.log('页面加载完成');
});
// 页面加载失败
win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
console.error('页面加载失败:', errorDescription);
});
// 页面标题更新
win.on('page-title-updated', (event, title) => {
console.log('页面标题:', title);
});
// 准备显示
win.on('ready-to-show', () => {
win.show();
});
4.3 交互事件 #
javascript
// 窗口移动
win.on('moved', (event, newBounds) => {
console.log('窗口移动到:', newBounds);
});
// 窗口缩放
win.on('resized', () => {
const [width, height] = win.getSize();
console.log(`窗口大小: ${width}x${height}`);
});
// 拖拽文件
win.on('file-drop', (event, paths) => {
console.log('拖拽文件:', paths);
});
// 阻止默认拖拽行为
win.webContents.on('will-navigate', (event, url) => {
event.preventDefault();
});
// 新窗口请求
win.webContents.setWindowOpenHandler(({ url }) => {
// 在默认浏览器中打开
shell.openExternal(url);
return { action: 'deny' };
});
五、窗口管理器 #
5.1 简单窗口管理器 #
javascript
// windowManager.js
const { BrowserWindow } = require('electron');
const path = require('path');
class WindowManager {
constructor() {
this.windows = new Map();
}
create(name, options) {
const win = new BrowserWindow({
...options,
webPreferences: {
preload: path.join(__dirname, '../preload/index.js'),
nodeIntegration: false,
contextIsolation: true,
...options.webPreferences
}
});
this.windows.set(name, win);
win.on('closed', () => {
this.windows.delete(name);
});
return win;
}
get(name) {
return this.windows.get(name);
}
has(name) {
return this.windows.has(name);
}
close(name) {
const win = this.windows.get(name);
if (win) {
win.close();
}
}
closeAll() {
for (const win of this.windows.values()) {
win.close();
}
}
broadcast(channel, data) {
for (const win of this.windows.values()) {
if (!win.isDestroyed()) {
win.webContents.send(channel, data);
}
}
}
get all() {
return Array.from(this.windows.values());
}
get count() {
return this.windows.size;
}
}
module.exports = new WindowManager();
5.2 使用窗口管理器 #
javascript
// main.js
const windowManager = require('./windowManager');
app.whenReady().then(() => {
// 创建主窗口
const mainWindow = windowManager.create('main', {
width: 800,
height: 600,
title: '主窗口'
});
mainWindow.loadFile('index.html');
// 创建设置窗口(延迟创建)
ipcMain.handle('open-settings', () => {
if (windowManager.has('settings')) {
windowManager.get('settings').focus();
return;
}
const settingsWin = windowManager.create('settings', {
width: 500,
height: 400,
title: '设置',
parent: windowManager.get('main'),
modal: true
});
settingsWin.loadFile('settings.html');
});
});
六、无边框窗口 #
6.1 创建无边框窗口 #
javascript
const framelessWin = new BrowserWindow({
width: 800,
height: 600,
frame: false,
transparent: false,
resizable: true
});
6.2 自定义标题栏 #
html
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<style>
.titlebar {
-webkit-app-region: drag;
height: 32px;
background: #2c3e50;
display: flex;
align-items: center;
padding: 0 10px;
}
.titlebar-title {
color: white;
font-size: 13px;
flex: 1;
}
.titlebar-controls {
-webkit-app-region: no-drag;
display: flex;
}
.titlebar-button {
width: 46px;
height: 32px;
border: none;
background: transparent;
color: white;
cursor: pointer;
}
.titlebar-button:hover {
background: rgba(255, 255, 255, 0.1);
}
.titlebar-button.close:hover {
background: #e74c3c;
}
</style>
</head>
<body>
<div class="titlebar">
<span class="titlebar-title">我的应用</span>
<div class="titlebar-controls">
<button class="titlebar-button minimize" onclick="minimizeWindow()">─</button>
<button class="titlebar-button maximize" onclick="maximizeWindow()">□</button>
<button class="titlebar-button close" onclick="closeWindow()">✕</button>
</div>
</div>
<div class="content">
<!-- 应用内容 -->
</div>
<script>
const { ipcRenderer } = require('electron');
function minimizeWindow() {
ipcRenderer.send('window:minimize');
}
function maximizeWindow() {
ipcRenderer.send('window:maximize');
}
function closeWindow() {
ipcRenderer.send('window:close');
}
</script>
</body>
</html>
javascript
// main.js - 处理窗口控制
ipcMain.on('window:minimize', (event) => {
BrowserWindow.fromWebContents(event.sender).minimize();
});
ipcMain.on('window:maximize', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win.isMaximized()) {
win.unmaximize();
} else {
win.maximize();
}
});
ipcMain.on('window:close', (event) => {
BrowserWindow.fromWebContents(event.sender).close();
});
七、窗口状态持久化 #
7.1 保存和恢复窗口状态 #
javascript
// windowState.js
const { app } = require('electron');
const fs = require('fs');
const path = require('path');
const statePath = path.join(app.getPath('userData'), 'window-state.json');
function loadState() {
try {
return JSON.parse(fs.readFileSync(statePath, 'utf-8'));
} catch {
return {
width: 800,
height: 600,
x: undefined,
y: undefined,
isMaximized: false
};
}
}
function saveState(win) {
const state = {
...win.getBounds(),
isMaximized: win.isMaximized()
};
fs.writeFileSync(statePath, JSON.stringify(state));
}
module.exports = { loadState, saveState };
7.2 使用窗口状态 #
javascript
// main.js
const { loadState, saveState } = require('./windowState');
function createWindow() {
const state = loadState();
const win = new BrowserWindow({
width: state.width,
height: state.height,
x: state.x,
y: state.y,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true
}
});
if (state.isMaximized) {
win.maximize();
}
// 监听窗口变化
win.on('close', () => {
saveState(win);
});
win.on('moved', () => saveState(win));
win.on('resized', () => saveState(win));
win.loadFile('index.html');
return win;
}
八、最佳实践 #
8.1 延迟显示 #
javascript
function createWindow() {
const win = new BrowserWindow({
show: false, // 先不显示
width: 800,
height: 600
});
win.loadFile('index.html');
// 页面准备好后再显示
win.once('ready-to-show', () => {
win.show();
});
return win;
}
8.2 单例窗口 #
javascript
let settingsWindow = null;
function openSettings() {
if (settingsWindow) {
settingsWindow.focus();
return;
}
settingsWindow = new BrowserWindow({
width: 500,
height: 400
});
settingsWindow.on('closed', () => {
settingsWindow = null;
});
settingsWindow.loadFile('settings.html');
}
8.3 窗口关闭确认 #
javascript
win.on('close', (event) => {
if (hasUnsavedChanges) {
const choice = dialog.showMessageBoxSync(win, {
type: 'question',
buttons: ['保存', '不保存', '取消'],
title: '确认',
message: '有未保存的更改,是否保存?'
});
if (choice === 0) {
event.preventDefault();
saveChanges(() => win.close());
} else if (choice === 2) {
event.preventDefault();
}
}
});
九、总结 #
9.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 配置选项 | 尺寸、位置、外观、行为等 |
| 安全配置 | contextIsolation、nodeIntegration 等 |
| 窗口方法 | 显示、隐藏、关闭、尺寸、位置 |
| 窗口事件 | 生命周期、加载、交互事件 |
| 窗口管理 | 使用管理器统一管理窗口 |
9.2 下一步 #
现在你已经掌握了 BrowserWindow 的使用,接下来让我们学习 应用生命周期,深入了解 Electron 应用的生命周期管理!
最后更新:2026-03-28