«
Electron 写一个 点击按钮打开新窗口 的 Demo

时间:2023-8-10     作者:Fotink     分类:


背景

Electron 依赖 Chromium 的更新迭代,很多功能用法都在不断地变更
比如我在 bilibili 学习 Electron入门到精通(有可能是全站最好的electron课程) 的时候,发现网上的教程清一色都是使用 remote 进行进程间通信
而 Electron 早在 14 版本就移除了 remote 模块,官方推荐的方法是使用 ipcMain 和 ipcRender 进行进程间通信(详细请看官方文档
虽然也有办法继续使用 remote 模块,但这显然不是一个长久之计。所以咱们最好还是学着用 ipcMain 和 ipcRender

进程通信

可以参考上述官方文档,实现进程间简单的消息的发送和接受
那么该如何实现点击按钮创建一个新窗口的目的呢?很简单
思路之一是,在主进程的 main.js 中预编写一段可以与其它进程通信的代码,当收到其它进程的特定通信内容时,咱们就执行这段代码,创建一个新窗口,就跟上一篇创建 Hello World 的主窗口是一样的~
这里写了一个点击主界面中的设置按钮,打开设置窗口,再点击一次关闭设置窗口的 Demo,各部分代码如下

main.js

const { app, BrowserWindow, ipcMain } = require('electron')
// 主进程需使用 ipcMain ↑

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    autoHideMenuBar: true,  // 隐藏菜单栏
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  })
  win.loadFile('./html/index.html')
}

const controlSettingsWindow = () => {
  if (!settingsWin) {
    settingsWin = new BrowserWindow({
      width: 500,
      height: 300,
      autoHideMenuBar: true,  // 隐藏菜单栏
      webPreferences: {
        nodeIntegration: true,
        contextIsolation: false
      }
    });
    settingsWin.loadFile('./settings.html');    // 加载 settings.html
  } else {
    settingsWin.destroy();      // 再次点击时关闭该窗口
    settingsWin = null;
  }
}

// 进程间通信开始↓
// 创建设置窗口(settings)
let settingsWin = null;
ipcMain.on('settings-window', () => {
  controlSettingsWindow();  // 调用设置窗口创建和关闭的函数
});
// 进程间通信结束↑

app.whenReady().then(() => {
  createWindow()
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta
      http-equiv="Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <meta
      http-equiv="X-Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <title>Electron Test Demo</title>
  </head>
  <body>
    <h1>👋你好啊</h1>
    <button id="open-settings-btn">点击打开设置窗口</button>
    <script src="./index.js"></script>
    <p id="info"></p>
  </body>
</html>

index.js

const { ipcRenderer } = require('electron')
// 子进程(渲染进程)中使用 ipcRender

window.addEventListener('DOMContentLoaded', () => {
    const sBtn = document.getElementById('open-settings-btn')
    sBtn.addEventListener('click', () => {
        // 调用主进程中,预编写的函数实现打开窗口
        ipcRenderer.send('settings-window');
    })
})

settings.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>设置页面 | Electron Test Demo</title>
</head>
<body>
    <h1>这里是设置页面~</h1>
    <script src="./settings.js"></script>
</body>
</html>

settings.js

const { ipcRenderer } = require('electron')

window.addEventListener('DOMContentLoaded', () => {
    // 拦截点击该窗口关闭按钮执行的动作
    window.onbeforeunload = function () {
        ipcRenderer.send('settings-window');
        // 由主进程 main.js 进行该窗口的 destroy() 关闭操作,否则会因为该窗口不为 null 而无法进行第二次打开窗口的操作
    }
})

写在结尾

因为我也是刚开始学 Electron 编写桌面端应用,且我并不擅长 JavaScript,所以关于这个 Demo 如果你有更好的方式方法,欢迎留言告诉我~