Electron 创建和管理窗口

窗口配置项、多窗口管理、父子窗口、无边框窗口与模态框。

Intermediate
Key Points每个 BrowserWindow 是一个独立窗口;父窗口关闭时子窗口一起关,模态窗口会禁用父窗口交互。

常用配置

OptionEffect
width / height初始大小
minWidth / minHeight防止拖到不可用
frame: false自己画标题栏
transparent: true圆角、异形窗口
resizablefalse 固定大小
alwaysOnTop悬浮窗
parent / modal子窗口跟随父窗口
show: false配合 ready-to-show 防白屏

创建窗口

JavaScript
const { BrowserWindow } = require('electron');
const path = require('node:path');

function createMainWindow() {
  const win = new BrowserWindow({
    width: 1200, height: 800,
    minWidth: 900, minHeight: 600,
    show: false,
    backgroundColor: '#0f172a',
    titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default',
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  });

  win.once('ready-to-show', () => win.show());
  win.loadFile('index.html');
  return win;
}

多窗口管理

JavaScript
const windows = new Map();

function openWindow(id) {
  if (windows.has(id)) {
    const w = windows.get(id);
    if (w.isMinimized()) w.restore();
    w.focus();
    return w;
  }
  const w = new BrowserWindow({ width: 800, height: 600, show: false });
  w.loadFile(`pages/${id}.html`);
  w.once('ready-to-show', () => w.show());
  w.on('closed', () => windows.delete(id));      // 必须清理,否则引用泄漏
  windows.set(id, w);
  return w;
}

Track windows in a Map and focus the existing one instead of opening another. Delete the reference on closed, or closed windows linger in memory and windows.size only grows.

模态窗口

JavaScript
function openSettings(parent) {
  const dlg = new BrowserWindow({
    width: 480, height: 360,
    parent,                 // 跟随父窗口
    modal: true,            // 父窗口被禁用,必须先处理这个
    resizable: false,
    minimizable: false,
    maximizable: false,
    show: false
  });
  dlg.setMenuBarVisibility(false);
  dlg.loadFile('settings.html');
  dlg.once('ready-to-show', () => dlg.show());
}

With modal: true clicks on the parent do nothing until the child closes. Good for mandatory confirmations; overuse makes the app feel frozen, so avoid it for everyday actions.

无边框窗口

CSS
/* 自绘标题栏:留出可拖拽区域 */
.titlebar {
  height: 38px;
  -webkit-app-region: drag;         /* 整条可拖 */
  background: #111827;
  display: flex; align-items: center; padding: 0 12px;
}
.titlebar button {
  -webkit-app-region: no-drag;      /* 按钮不能拖,否则点不到 */
}
HTML
<div class="titlebar">
  <span>我的应用</span>
  <button id="min">—</button>
  <button id="max">□</button>
  <button id="close">×</button>
</div>

-webkit-app-region: drag makes a region draggable, but buttons inside must be marked no-drag or the drag logic swallows their clicks — the most common borderless-window bug.

Persist window size and position and restore them next launch: write JSON under app.getPath('userData') on close, read it when creating. Users have muscle memory for window geometry.

窗口事件

JavaScript
win.on('close', (e) => {
  // 有未保存内容时拦一下
  if (hasUnsavedChanges) {
    e.preventDefault();
    const choice = dialog.showMessageBoxSync(win, {
      type: 'warning',
      buttons: ['取消', '不保存退出'],
      defaultId: 0,
      message: '有未保存的修改,确定退出?'
    });
    if (choice === 1) { hasUnsavedChanges = false; win.close(); }
  }
});

win.on('closed', () => { mainWindow = null; });
win.on('focus', () => { /* 恢复轮询 */ });
win.on('blur', () => { /* 暂停轮询 */ });

close can be preventDefaulted; closed means it is gone for good. Mixing them up gives you a double confirmation dialog or no interception at all.

Friends & Partners