第4章:Electron主窗口与子窗口管理

发布于:2024-07-03 ⋅ 阅读:(17) ⋅ 点赞:(0)

4.1 创建主窗口

主窗口是 Electron 应用启动后显示的第一个窗口,通常用来承载应用的主界面。我们使用 BrowserWindow 类来创建主窗口。

4.1.1 创建主窗口的基础代码

// 引入 Electron 模块和 Node.js 的 path 模块
const { app, BrowserWindow } = require('electron');
const path = require('path');

// 定义一个变量用于存储主窗口对象
let mainWindow;

// 创建主窗口的函数
const createMainWindow = () => {
  // 实例化 BrowserWindow 对象
  mainWindow = new BrowserWindow({
    width: 800, // 窗口宽度
    height: 600, // 窗口高度
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'), // 指定预加载脚本
      contextIsolation: true, // 启用上下文隔离
      nodeIntegration: false // 禁用 Node.js 集成
    }
  });

  // 加载主窗口的 HTML 文件
  mainWindow.loadFile('index.html');

  // 打开开发者工具(仅在开发阶段使用)
  mainWindow.webContents.openDevTools();

  // 监听主窗口的关闭事件
  mainWindow.on('closed', () => {
    // 当窗口被关闭时,将 mainWindow 设置为 null
    mainWindow = null;
  });
};

// 当 Electron 完成初始化并准备创建浏览器窗口时,调用 createMainWindow 函数
app.on('ready', createMainWindow);

// 当所有窗口关闭时,退出应用(除非在 macOS 上)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

// 当应用被激活时(例如在 macOS 上单击应用图标),重新创建主窗口
app.on('activate', () => {
  if (mainWindow === null) {
    createMainWindow();
  }
});

4.2 创建子窗口

子窗口用于显示辅助内容或执行辅助任务。与主窗口类似,子窗口也是通过 BrowserWindow 类创建的。

4.2.1 创建子窗口的示例代码

// 创建子窗口的函数
const createChildWindow = () => {
  // 实例化 BrowserWindow 对象
  let childWindow = new BrowserWindow({
    parent: mainWindow, // 设置父窗口为主窗口
    modal: true, // 设置为模态窗口
    width: 400, // 窗口宽度
    height: 300, // 窗口高度
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'), // 指定预加载脚本
      contextIsolation: true, // 启用上下文隔离
      nodeIntegration: false // 禁用 Node.js 集成
    }
  });

  // 加载子窗口的 HTML 文件
  childWindow.loadFile('child.html');

  // 监听子窗口的关闭事件
  childWindow.on('closed', () => {
    // 当窗口被关闭时,将 childWindow 设置为 null
    childWindow = null;
  });
};

// 在主窗口创建完成后创建子窗口
app.on('ready', () => {
  createMainWindow();
  createChildWindow();
});

4.3 窗口间通信

通过 IPC 机制,主窗口和子窗口可以相互通信。这里使用 ipcMainipcRenderer 模块实现通信。

4.3.1 主窗口与子窗口之间的通信示例

主进程:

const { ipcMain } = require('electron');

// 监听从渲染进程发送的消息
ipcMain.on('message-from-child', (event, arg) => {
  console.log('Received message from child:', arg);
  // 回复消息到渲染进程
  event.reply('reply-from-main', 'Message received by main process');
});

4.4 预加载脚本

预加载脚本在渲染进程加载前执行,允许在渲染器上下文中暴露自定义 API,并提供与主进程安全通信的桥梁。

4.4.1 创建预加载脚本

preload.js

const { contextBridge, ipcRenderer } = require('electron');

// 使用 contextBridge 将安全的 API 暴露给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
  sendMessage: (message) => ipcRenderer.send('message-from-child', message),
  onReply: (callback) => ipcRenderer.on('reply-from-main', (event, args) => callback(args))
});

4.4.2 在渲染进程中使用预加载脚本

子窗口(渲染进程):

<!DOCTYPE html>
<html>
  <head>
    <title>Child Window</title>
  </head>
  <body>
    <h1>Child Window</h1>
    <button id="sendMessageBtn">Send Message to Main</button>
    <script>
      // 使用预加载脚本暴露的 API
      document.getElementById('sendMessageBtn').addEventListener('click', () => {
        window.electronAPI.sendMessage('Hello from child window');
      });

      window.electronAPI.onReply((message) => {
        console.log('Received reply from main:', message);
      });
    </script>
  </body>
</html>

4.5 管理多个窗口

在复杂的应用中,可能需要同时管理多个窗口。可以通过存储窗口实例的数组或对象来实现这一点。

4.5.1 管理多个窗口的示例

const windows = {};

// 创建子窗口的函数
const createChildWindow = (windowName) => {
  let childWindow = new BrowserWindow({
    parent: mainWindow,
    modal: true,
    width: 400,
    height: 300,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  });

  childWindow.loadFile('child.html');

  childWindow.on('closed', () => {
    // 当窗口被关闭时,从 windows 对象中删除对应的实例
    delete windows[windowName];
  });

  // 将窗口实例存储到 windows 对象中
  windows[windowName] = childWindow;
};

// 创建多个子窗口
app.on('ready', () => {
  createMainWindow();
  createChildWindow('child1');
  createChildWindow('child2');
});

4.6 窗口的显示和隐藏

有时需要在应用中显示或隐藏窗口,而不是创建或销毁它们。

4.6.1 显示和隐藏窗口的示例

// 显示子窗口
const showChildWindow = (windowName) => {
  if (windows[windowName]) {
    windows[windowName].show();
  }
};

// 隐藏子窗口
const hideChildWindow = (windowName) => {
  if (windows[windowName]) {
    windows[windowName].hide();
  }
};

// 在主窗口创建完成后创建子窗口并演示显示和隐藏功能
app.on('ready', () => {
  createMainWindow();
  createChildWindow('child1');

  // 隐藏子窗口 child1
  hideChildWindow('child1');

  // 2 秒后显示子窗口 child1
  setTimeout(() => {
    showChildWindow('child1');
  }, 2000);
});

通过本章内容,你已经了解了如何在 Electron 中创建和管理主窗口及子窗口,包括如何进行窗口间通信、使用预加载脚本提高安全性、管理多个窗口以及显示和隐藏窗口的操作。在接下来的章节中,我们将进一步探讨如何实现更多高级功能和最佳实践,帮助你进一步掌握 Electron 开发。