第3章:窗口管理与 BrowserWindow
掌握 BrowserWindow API,学会创建和管理各种类型的应用窗口
3.1 BrowserWindow 基础
BrowserWindow 是 Electron 中最重要的类之一,它代表一个应用窗口,负责创建和控制浏览器窗口。
基本概念
- 每个 BrowserWindow 实例代表一个应用窗口
- 每个窗口运行在独立的渲染进程中
- 窗口可以加载本地文件或远程 URL
- 支持丰富的配置选项和事件处理
创建基本窗口
javascript
const { BrowserWindow } = require('electron')
function createWindow() {
const window = new BrowserWindow({
width: 800,
height: 600,
title: '我的应用窗口'
})
window.loadFile('index.html')
return window
}
3.2 窗口配置选项
BrowserWindow 提供了丰富的配置选项来定制窗口的外观和行为。
尺寸和位置
javascript
const window = new BrowserWindow({
// 窗口尺寸
width: 1200,
height: 800,
minWidth: 400,
minHeight: 300,
maxWidth: 1600,
maxHeight: 1200,
// 窗口位置
x: 100,
y: 100,
center: true, // 居中显示
// 尺寸限制
resizable: true,
minimizable: true,
maximizable: true,
closable: true
})
外观定制
javascript
const window = new BrowserWindow({
// 标题栏
title: '自定义标题',
titleBarStyle: 'default', // 'default', 'hidden', 'hiddenInset'
// 窗口样式
frame: true, // 是否显示边框
transparent: false, // 透明窗口
opacity: 1.0, // 不透明度 (macOS/Linux)
// 图标
icon: path.join(__dirname, 'assets/icon.png'),
// 显示状态
show: true, // 创建后立即显示
modal: false, // 模态窗口
alwaysOnTop: false, // 总是置顶
// 背景色
backgroundColor: '#ffffff'
})
高级配置
javascript
const window = new BrowserWindow({
// Web 偏好设置
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
preload: path.join(__dirname, 'preload.js'),
sandbox: false,
webSecurity: true,
allowRunningInsecureContent: false,
experimentalFeatures: false
},
// 其他选项
acceptFirstMouse: false, // 是否接受第一次鼠标点击
disableAutoHideCursor: false, // 禁用自动隐藏光标
enableLargerThanScreen: false, // 允许窗口大于屏幕
hasShadow: true, // 窗口阴影
thickFrame: true, // 厚边框 (Windows)
vibrancy: 'appearance-based', // 毛玻璃效果 (macOS)
zoomToPageWidth: false, // 缩放到页面宽度
tabbingIdentifier: 'main' // 标签组标识符 (macOS)
})
3.3 窗口生命周期管理
窗口事件处理
javascript
class WindowManager {
constructor() {
this.windows = new Map()
}
createWindow(id, options = {}) {
const defaultOptions = {
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
}
const window = new BrowserWindow({
...defaultOptions,
...options
})
// 设置窗口事件监听
this.setupWindowEvents(window, id)
this.windows.set(id, window)
return window
}
setupWindowEvents(window, id) {
// 窗口准备显示
window.once('ready-to-show', () => {
console.log(`窗口 ${id} 准备显示`)
window.show()
})
// 窗口关闭
window.on('closed', () => {
console.log(`窗口 ${id} 已关闭`)
this.windows.delete(id)
})
// 窗口最小化
window.on('minimize', () => {
console.log(`窗口 ${id} 已最小化`)
})
// 窗口最大化
window.on('maximize', () => {
console.log(`窗口 ${id} 已最大化`)
})
// 窗口恢复
window.on('restore', () => {
console.log(`窗口 ${id} 已恢复`)
})
// 窗口获得焦点
window.on('focus', () => {
console.log(`窗口 ${id} 获得焦点`)
})
// 窗口失去焦点
window.on('blur', () => {
console.log(`窗口 ${id} 失去焦点`)
})
// 窗口移动
window.on('move', () => {
const [x, y] = window.getPosition()
console.log(`窗口 ${id} 移动到 (${x}, ${y})`)
})
// 窗口调整大小
window.on('resize', () => {
const [width, height] = window.getSize()
console.log(`窗口 ${id} 调整大小为 ${width}x${height}`)
})
}
getWindow(id) {
return this.windows.get(id)
}
closeWindow(id) {
const window = this.windows.get(id)
if (window) {
window.close()
}
}
closeAllWindows() {
this.windows.forEach(window => window.close())
}
}
// 使用示例
const windowManager = new WindowManager()
3.4 多窗口应用
窗口类型和用途
javascript
class MultiWindowApp {
constructor() {
this.mainWindow = null
this.settingsWindow = null
this.aboutWindow = null
}
// 主窗口
createMainWindow() {
this.mainWindow = new BrowserWindow({
width: 1200,
height: 800,
title: '主窗口',
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
})
this.mainWindow.loadFile('main.html')
this.mainWindow.on('closed', () => {
this.mainWindow = null
})
return this.mainWindow
}
// 设置窗口
createSettingsWindow() {
if (this.settingsWindow) {
this.settingsWindow.focus()
return this.settingsWindow
}
this.settingsWindow = new BrowserWindow({
width: 600,
height: 400,
title: '设置',
parent: this.mainWindow, // 设置父窗口
modal: true, // 模态窗口
resizable: false,
minimizable: false,
maximizable: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
})
this.settingsWindow.loadFile('settings.html')
this.settingsWindow.on('closed', () => {
this.settingsWindow = null
})
return this.settingsWindow
}
// 关于窗口
createAboutWindow() {
if (this.aboutWindow) {
this.aboutWindow.focus()
return this.aboutWindow
}
this.aboutWindow = new BrowserWindow({
width: 400,
height: 300,
title: '关于',
parent: this.mainWindow,
modal: false,
resizable: false,
minimizable: false,
maximizable: false,
alwaysOnTop: true,
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
this.aboutWindow.loadFile('about.html')
this.aboutWindow.on('closed', () => {
this.aboutWindow = null
})
return this.aboutWindow
}
// 子窗口
createChildWindow(parentWindow) {
const childWindow = new BrowserWindow({
width: 400,
height: 300,
parent: parentWindow,
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
childWindow.loadFile('child.html')
return childWindow
}
}
窗口间通信
javascript
// 主进程中的窗口通信管理
class WindowCommunication {
constructor() {
this.windows = new Map()
this.setupIPC()
}
setupIPC() {
// 窗口间消息转发
ipcMain.on('window-message', (event, data) => {
const { targetWindow, message } = data
const target = this.windows.get(targetWindow)
if (target) {
target.webContents.send('window-message', message)
}
})
// 广播消息到所有窗口
ipcMain.on('broadcast-message', (event, message) => {
this.windows.forEach(window => {
window.webContents.send('broadcast-message', message)
})
})
}
registerWindow(id, window) {
this.windows.set(id, window)
}
unregisterWindow(id) {
this.windows.delete(id)
}
}
3.5 窗口状态管理
保存和恢复窗口状态
javascript
const Store = require('electron-store')
const store = new Store()
class WindowStateManager {
constructor(windowId, defaultState = {}) {
this.windowId = windowId
this.defaultState = {
width: 800,
height: 600,
x: undefined,
y: undefined,
isMaximized: false,
isMinimized: false,
...defaultState
}
}
// 获取保存的窗口状态
getState() {
const savedState = store.get(`windowState.${this.windowId}`)
return { ...this.defaultState, ...savedState }
}
// 保存窗口状态
saveState(window) {
const bounds = window.getBounds()
const state = {
...bounds,
isMaximized: window.isMaximized(),
isMinimized: window.isMinimized()
}
store.set(`windowState.${this.windowId}`, state)
}
// 应用状态到窗口
applyState(window) {
const state = this.getState()
// 设置窗口位置和大小
window.setBounds({
x: state.x,
y: state.y,
width: state.width,
height: state.height
})
// 恢复最大化状态
if (state.isMaximized) {
window.maximize()
}
}
// 监听窗口状态变化
watchState(window) {
const saveState = () => this.saveState(window)
window.on('resize', saveState)
window.on('move', saveState)
window.on('maximize', saveState)
window.on('unmaximize', saveState)
window.on('minimize', saveState)
window.on('restore', saveState)
}
}
// 使用示例
function createWindowWithState() {
const stateManager = new WindowStateManager('main')
const state = stateManager.getState()
const window = new BrowserWindow({
...state,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
// 应用保存的状态
stateManager.applyState(window)
// 监听状态变化
stateManager.watchState(window)
window.once('ready-to-show', () => {
window.show()
})
return window
}
3.6 特殊窗口类型
无边框窗口
javascript
const framelessWindow = new BrowserWindow({
width: 800,
height: 600,
frame: false, // 无边框
titleBarStyle: 'hidden',
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
// 自定义拖拽区域 (在 CSS 中)
/*
.titlebar {
-webkit-app-region: drag;
height: 30px;
background: #333;
}
.titlebar button {
-webkit-app-region: no-drag;
}
*/
透明窗口
javascript
const transparentWindow = new BrowserWindow({
width: 800,
height: 600,
transparent: true,
frame: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
// 设置透明背景 (在 CSS 中)
/*
body {
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
}
*/
工具窗口
javascript
const toolWindow = new BrowserWindow({
width: 300,
height: 200,
type: 'toolbar', // 工具窗口类型
alwaysOnTop: true,
skipTaskbar: true,
resizable: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
3.7 窗口性能优化
延迟显示
javascript
const window = new BrowserWindow({
show: false, // 创建时不显示
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
// 页面准备好后再显示
window.once('ready-to-show', () => {
window.show()
})
预加载优化
javascript
// 预创建隐藏窗口
class WindowPool {
constructor() {
this.pool = []
this.preloadCount = 2
this.preloadWindows()
}
preloadWindows() {
for (let i = 0; i < this.preloadCount; i++) {
const window = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
this.pool.push(window)
}
}
getWindow() {
if (this.pool.length > 0) {
const window = this.pool.pop()
// 补充池中的窗口
this.preloadWindows()
return window
}
// 池中没有窗口时创建新的
return new BrowserWindow({
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
}
}
内存管理
javascript
// 及时清理不需要的窗口
function cleanupWindow(window) {
if (window && !window.isDestroyed()) {
window.removeAllListeners()
window.close()
}
}
// 监控内存使用
function monitorMemory() {
const memoryUsage = process.memoryUsage()
console.log('内存使用情况:', {
rss: `${Math.round(memoryUsage.rss / 1024 / 1024)} MB`,
heapTotal: `${Math.round(memoryUsage.heapTotal / 1024 / 1024)} MB`,
heapUsed: `${Math.round(memoryUsage.heapUsed / 1024 / 1024)} MB`
})
}
3.8 常见问题和解决方案
问题 1:窗口闪烁
原因:窗口创建后立即显示,但内容还未加载完成
解决方案:
javascript
const window = new BrowserWindow({
show: false,
backgroundColor: '#ffffff' // 设置背景色
})
window.once('ready-to-show', () => {
window.show()
})
问题 2:窗口位置不正确
原因:多显示器环境下窗口位置计算错误
解决方案:
javascript
const { screen } = require('electron')
function createWindowOnPrimaryDisplay() {
const primaryDisplay = screen.getPrimaryDisplay()
const { width, height } = primaryDisplay.workAreaSize
const window = new BrowserWindow({
x: Math.floor((width - 800) / 2),
y: Math.floor((height - 600) / 2),
width: 800,
height: 600
})
return window
}
问题 3:窗口无法关闭
原因:事件监听器阻止了窗口关闭
解决方案:
javascript
window.on('close', (event) => {
// 检查是否有未保存的数据
if (hasUnsavedData()) {
event.preventDefault()
// 显示确认对话框
showSaveDialog().then((result) => {
if (result.confirmed) {
window.destroy()
}
})
}
})
本章小结
通过本章学习,你应该已经:
- ✅ 掌握了 BrowserWindow 的基本用法和配置选项
- ✅ 学会了窗口生命周期管理和事件处理
- ✅ 了解了多窗口应用的开发模式
- ✅ 掌握了窗口状态的保存和恢复
- ✅ 学会了创建特殊类型的窗口
- ✅ 了解了窗口性能优化技巧
- ✅ 掌握了常见问题的解决方案
在下一章中,我们将学习进程间通信(IPC),这是 Electron 应用开发的核心技能!