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