窗口管理
窗口创建
基本配置
javascript
function createWindow() {
const win = new BrowserWindow({
width: 1200,
height: 800,
titleBarStyle: getConfigValue('titleBarStyle'),
trafficLight: false,
autoHideMenuBar: getConfigValue('autoHideMenuBar'),
frame: getConfigValue('frame'),
toolbar: getConfigValue('toolbar'),
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
sandbox: false,
webviewTag: true,
webSecurity: false,
devTools: true,
defaultEncoding: 'UTF-8',
spellcheck: false,
enableWebSQL: false,
backgroundThrottling: getConfigValue('enablePerformanceMode'),
preload: path.join(app.getAppPath(), 'src/preload/preload.js'),
},
});
// 设置图标
win.setIcon(getAppIcons());
// 最大化窗口
win.maximize();
return win;
}配置项说明
| 配置项 | 说明 | 默认值 |
|---|---|---|
width / height | 窗口初始尺寸 | 1200 x 800 |
titleBarStyle | 标题栏样式 | 从配置文件读取 |
autoHideMenuBar | 自动隐藏菜单栏 | 从配置文件读取 |
frame | 是否显示窗口边框 | 从配置文件读取 |
toolbar | 是否显示工具栏 | 从配置文件读取 |
WebPreferences 安全配置
javascript
webPreferences: {
nodeIntegration: false, // ❌ 禁用 Node 集成(安全)
contextIsolation: true, // ✅ 启用上下文隔离(安全)
sandbox: false, // 根据需求配置沙箱
webviewTag: true, // ✅ 启用 webview 标签
webSecurity: false, // ⚠️ 开发环境可禁用,生产环境建议启用
devTools: true, // ✅ 启用开发者工具
preload: 'path/to/preload.js' // ✅ 指定预加载脚本
}安全最佳实践:
- ✅
nodeIntegration: false+contextIsolation: true - ✅ 使用 preload 脚本暴露安全的 API
- ❌ 避免在渲染进程直接使用 Node.js 模块
窗口加载
加载模式
支持两种加载模式(详见 加载模式文档):
javascript
async function windowLoadByMode(win, url) {
const loadMode = getConfigValue('loadMode', LOAD_MODE.REMOTE);
if (loadMode === LOAD_MODE.REMOTE) {
// 远程模式:加载远程 URL
await windowLoadRemoteURL(win, url);
} else {
// 本地模式:启动本地 HTTP 服务器
await windowLoadLocalServer(win);
}
}加载远程 URL
javascript
async function windowLoadRemoteURL(win, url) {
try {
if (!url) throw new Error('远程URL不能为空');
logger.info(`开始加载远程URL: ${url}`);
await win.loadURL(url);
openDevToolsIfNeeded(win);
logger.info(`远程URL加载成功: ${url}`);
} catch (error) {
logger.error(`加载URL失败: ${url}`, error);
throw error;
}
}加载本地服务器
javascript
async function windowLoadLocalServer(win) {
try {
const distPath = path.dirname(LOCAL_WEB_INDEX_FILE);
// 检查资源是否存在
if (!fs.existsSync(distPath)) {
throw new Error(`本地资源目录不存在: ${distPath}`);
}
// 启动本地 Express 服务器
const localServer = LocalServer.getInstance();
const serverUrl = await localServer.start(distPath);
logger.info(`开始通过本地服务器加载: ${serverUrl}`);
await win.loadURL(serverUrl);
openDevToolsIfNeeded(win);
logger.info('本地服务器页面加载成功');
} catch (error) {
logger.error('启动本地服务器失败:', error);
throw error;
}
}错误处理
当窗口加载失败时,会加载错误兜底页面:
javascript
windowLoadByMode(win, url).catch((error) => {
logger.error(error.message);
// LoadErrorHandler 会自动处理并显示错误页面
});窗口事件处理器
LoadErrorHandler - 加载错误处理
javascript
const errorHandler = new LoadErrorHandler(win);
errorHandler.setOriginalUrl(url);功能:
- 监听页面加载失败事件
- 自动加载错误页面
- 提供重试功能
OtherActionHandler - 其他动作处理
javascript
new OtherActionHandler(win, FloatingBallService);功能:
- 处理窗口最小化、最大化、关闭等操作
- 与悬浮球服务集成
- 窗口状态管理
RightMenuHandler - 右键菜单
javascript
if (getConfigValue('showWindowRightMenu')) {
new RightMenuHandler(win, FloatingBallService);
}功能:
- 自定义右键上下文菜单
- 提供常用操作快捷入口
- 集成悬浮球功能
ZoomIndicator - 缩放指示器
javascript
if (getConfigValue('showZoomIndicator')) {
new ZoomIndicator(win);
}功能:
- 显示当前缩放级别
- 缩放操作时的视觉反馈
- 自动隐藏提示
NativeFocusHandler - 焦点处理
javascript
new NativeFocusHandler(win);功能:
- 修复原生 API 焦点问题
- 处理窗口激活/失活事件
- 确保输入框焦点正常
窗口操作
基本操作
javascript
// 最大化
win.maximize();
// 最小化
win.minimize();
// 恢复
win.restore();
// 显示/隐藏
win.show();
win.hide();
// 关闭
win.close();
// 全屏
win.setFullScreen(true);窗口尺寸
javascript
// 设置尺寸
win.setSize(1200, 800);
// 获取尺寸
const [width, height] = win.getSize();
// 设置最小尺寸
win.setMinimumSize(800, 600);
// 设置最大尺寸
win.setMaximumSize(1920, 1080);窗口位置
javascript
// 设置位置
win.setPosition(100, 100);
// 获取位置
const [x, y] = win.getPosition();
// 居中
win.center();窗口状态
javascript
// 检查状态
win.isMaximized();
win.isMinimized();
win.isFullScreen();
win.isVisible();
win.isFocused();
// 聚焦
win.focus();
// 模糊
win.blur();窗口缩放
缩放级别
javascript
// 获取当前缩放级别
const zoomLevel = win.webContents.getZoomLevel();
// 设置缩放级别(-3 到 3)
win.webContents.setZoomLevel(1); // 放大
win.webContents.setZoomLevel(-1); // 缩小
win.webContents.setZoomLevel(0); // 重置
// 缩放因子
const zoomFactor = win.webContents.getZoomFactor();
win.webContents.setZoomFactor(1.2); // 120%托盘菜单缩放控制
javascript
{
label: '放大',
accelerator: 'CmdOrCtrl+Plus',
click: () => {
const currentLevel = win.webContents.getZoomLevel();
win.webContents.setZoomLevel(currentLevel + 1);
}
},
{
label: '缩小',
accelerator: 'CmdOrCtrl+-',
click: () => {
const currentLevel = win.webContents.getZoomLevel();
win.webContents.setZoomLevel(currentLevel - 1);
}
}窗口进度条
任务栏进度条
javascript
// 设置进度(0.0 到 1.0)
win.setProgressBar(0.5); // 50%
// 显示不确定进度
win.setProgressBar(2);
// 移除进度条
win.setProgressBar(-1);
// 错误状态(Windows)
win.setProgressBar(0.5, { mode: 'error' });使用场景:
- 文件下载
- 更新安装
- 数据处理
窗口标题
javascript
// 设置标题
win.setTitle('应用名称 - 加载中...');
// 获取标题
const title = win.getTitle();
// 动态更新
win.webContents.on('page-title-updated', (event, title) => {
console.log('页面标题:', title);
});开发者工具
自动打开
javascript
function openDevToolsIfNeeded(win) {
if (!app.isPackaged) {
win.webContents.openDevTools();
}
}手动控制
javascript
// 打开
win.webContents.openDevTools();
// 关闭
win.webContents.closeDevTools();
// 切换
win.webContents.toggleDevTools();
// 分离模式
win.webContents.openDevTools({ mode: 'detach' });窗口图标
设置图标
javascript
// Windows/Linux
win.setIcon(getAppIcons());
// macOS(应用图标在打包时配置)
// icon: 'resources/icons/icon.icns'图标配置
javascript
// packages/main/src/config/app-icon-config.js
function getAppIcons(type = 'default') {
const platform = process.platform;
if (platform === 'win32') {
return type === 'small'
? path.join(ICON_DIR, 'icon-16x16.ico')
: path.join(ICON_DIR, 'icon.ico');
} else if (platform === 'darwin') {
return path.join(ICON_DIR, 'icon.icns');
} else {
return path.join(ICON_DIR, 'icon.png');
}
}窗口生命周期事件
创建和显示
javascript
// 准备显示
win.once('ready-to-show', () => {
win.show();
});
// 显示完成
win.on('show', () => {
logger.info('窗口已显示');
});最小化和恢复
javascript
// 最小化
win.on('minimize', () => {
logger.info('窗口已最小化');
});
// 恢复
win.on('restore', () => {
logger.info('窗口已恢复');
});聚焦和失焦
javascript
// 获得焦点
win.on('focus', () => {
logger.info('窗口获得焦点');
});
// 失去焦点
win.on('blur', () => {
logger.info('窗口失去焦点');
});关闭
javascript
// 关闭前
win.on('close', (event) => {
// 阻止关闭
event.preventDefault();
// 隐藏到托盘而不是退出
win.hide();
});
// 关闭后
win.on('closed', () => {
logger.info('窗口已关闭');
win = null;
});WebContents 事件
页面导航
javascript
// 即将导航
win.webContents.on('will-navigate', (event, url) => {
logger.info('即将导航到:', url);
// 阻止导航
// event.preventDefault();
});
// 导航完成
win.webContents.on('did-navigate', (event, url) => {
logger.info('导航完成:', url);
});页面加载
javascript
// 开始加载
win.webContents.on('did-start-loading', () => {
logger.info('页面开始加载');
});
// 加载完成
win.webContents.on('did-finish-load', () => {
logger.info('页面加载完成');
});
// 加载失败
win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
logger.error('页面加载失败:', { errorCode, errorDescription });
});新窗口
javascript
// 拦截新窗口
win.webContents.setWindowOpenHandler(({ url }) => {
logger.info('拦截新窗口:', url);
// 在默认浏览器中打开
shell.openExternal(url);
// 阻止在 Electron 中打开
return { action: 'deny' };
});崩溃处理
javascript
// 渲染进程崩溃
win.webContents.on('render-process-gone', (event, details) => {
logger.error('渲染进程崩溃:', details);
if (details.reason === 'crashed') {
// 重新加载页面
win.reload();
}
});
// 响应超时
win.webContents.on('unresponsive', () => {
logger.warn('页面无响应');
dialog
.showMessageBox(win, {
type: 'warning',
message: '页面无响应,是否等待?',
buttons: ['等待', '重新加载'],
})
.then(({ response }) => {
if (response === 1) win.reload();
});
});
// 响应恢复
win.webContents.on('responsive', () => {
logger.info('页面已恢复响应');
});多窗口管理
获取所有窗口
javascript
const { BrowserWindow } = require('electron');
// 获取所有窗口
const allWindows = BrowserWindow.getAllWindows();
// 获取聚焦的窗口
const focusedWindow = BrowserWindow.getFocusedWindow();子窗口
javascript
// 创建子窗口
const childWin = new BrowserWindow({
parent: win, // 设置父窗口
modal: true, // 模态窗口
width: 400,
height: 300,
});
// 子窗口会随父窗口一起最小化/关闭窗口通信
主进程向渲染进程发送消息
javascript
// 发送消息
win.webContents.send('channel-name', data);
// 广播到所有窗口
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send('channel-name', data);
});渲染进程向主进程发送消息
详见 IPC通信文档
性能优化
1. 延迟显示
javascript
const win = new BrowserWindow({
show: false, // 创建时不显示
});
win.once('ready-to-show', () => {
win.show(); // 准备好后再显示,避免闪烁
});2. 后台节流
javascript
webPreferences: {
// 禁用后台节流,保持性能
backgroundThrottling: false;
}3. 硬件加速
javascript
// 启用硬件加速(默认开启)
app.disableHardwareAcceleration(); // 禁用(特殊情况)4. 资源预加载
javascript
// 在 ready-to-show 之前预加载资源
win.webContents.on('did-finish-load', () => {
// 预加载常用资源
});最佳实践
1. 安全配置
javascript
✅ 推荐配置
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
sandbox: true, // 生产环境启用
preload: 'preload.js'
}
❌ 不推荐
webPreferences: {
nodeIntegration: true, // 安全风险
contextIsolation: false // 安全风险
}2. 窗口状态持久化
javascript
// 保存窗口状态
app.on('before-quit', () => {
const [x, y] = win.getPosition();
const [width, height] = win.getSize();
store.set('windowBounds', { x, y, width, height });
});
// 恢复窗口状态
const bounds = store.get('windowBounds');
if (bounds) {
win.setBounds(bounds);
}3. 优雅的关闭
javascript
win.on('close', (event) => {
if (!app.isQuitting) {
event.preventDefault();
win.hide();
}
});
app.on('before-quit', () => {
app.isQuitting = true;
});4. 资源清理
javascript
win.on('closed', () => {
// 清理引用,释放内存
win = null;
// 清理相关资源
errorHandler = null;
updateHandler = null;
});相关文件
packages/main/src/core/window.js- 窗口创建和管理packages/main/src/handlers/load-error-handler.js- 加载错误处理packages/main/src/handlers/other-action-handler.js- 窗口动作处理packages/main/src/handlers/right-menu-handler.js- 右键菜单packages/main/src/handlers/native-focus-handler.js- 焦点处理packages/main/src/indicator/zoom-indicator.js- 缩放指示器