介绍

Electron是一个运用 JavaScript、HTML 和 CSS 构建桌面运用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您坚持一个 JavaScript 代码代码库并创立 在Windows上运转的跨渠道运用 macOS和Linux——不需要原生桌面端开发经历。

装置

npm install --save-dev electron

因为众所周知的原因,你或许装置不上依靠,此刻能够设置镜像。

首要检查你的镜像源。

nrm ls

保证你运用的镜像源是 npm 源。

翻开npm的装备文件:

npm config edit

在里面新增如下装备:

registry=https://registry.npmmirror.com
electron_mirror=https://cdn.npmmirror.com/binaries/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

electron 入门教程

然后封闭窗口,重新装置依靠。

创立第一个运用程序

在 Electron 中,每个窗口展现一个页面,后者能够来自本地的 HTML,也能够来自长途 URL。 在本例中,您将会装载本地的文件。 在您项目的根目录中创立一个 index.html 文件,并写入下面的内容:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <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>Hello from Electron renderer!</title>
  </head>
  <body>
    <h1>Hello from Electron renderer!</h1>
    <p></p>
  </body>
</html>

在根目录下面新增 index.js,内容如下:

const { app, BrowserWindow } = require('electron')
// createWindow() 函数将您的页面加载到新的 BrowserWindow 实例中
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600
  })
  win.loadFile('index.html')
}
// 在运用准备就绪时调用函数
app.whenReady().then(() => {
    createWindow()
    // 假如没有窗口翻开则翻开一个窗口 (macOS)
    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
            createWindow()
        }
    })
})
// 封闭一切窗口时退出运用 (Windows & Linux)
// 监听 app 模块的 window-all-closed 事情,并调用 app.quit() 来退出您的运用程序。此办法不适用于 macOS
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') app.quit()
})
  • app: 办理运用程序的事情生命周期。
  • BrowserWindow: 负责创立和办理运用窗口。

装备 package.json 的文件:

{
  "scripts": {
    "main": "index.js",
    "start": "electron ."
  }
}

现在运转 npm run start 就能够启动项目了。

运用预加载脚本

Electron 的主进程是一个拥有着完全操作系统拜访权限的 Node.js 环境。 除了 Electron 模组 之外,您也能够拜访 Node.js 内置模块 和一切经过 npm 装置的包。 另一方面,出于安全原因,烘托进程默认跑在网页页面上,而并非 Node.js里。

BrowserWindow 的预加载脚本运转在具有 HTML DOM 和 Node.js、Electron API 的有限子集拜访权限的环境中。

预加载脚本在烘托器加载网页之前注入。 假如你想为烘托器添加需要特别权限的功能,能够经过 contextBridge 接口界说 大局目标。

在项目的根目录新建一个 preload.js 文件。该脚本经过 versions 这一大局变量,将 Electron 的 process.versions 目标露出给烘托器。

// preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron
  // 除函数之外,咱们也能够露出变量
})

为了将脚本附在烘托进程上,在 BrowserWindow 结构器中运用 webPreferences.preload 传入脚本的途径。

// index.js
const { app, BrowserWindow } = require('electron')
// 新增下面这行代码
const path = require('node:path')
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600, 
    // 新增
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })
  win.loadFile('index.html')
}
app.whenReady().then(() => {
  createWindow()
})

在项目的根目录新增一个 renderer.js 文件,内容如下:

const information = document.getElementById('info')
information.innerText = `本运用正在运用 Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), 和 Electron (v${versions.electron()})`

然后把 renderer.js 脚本插入到 index.html 文件的 body 标签后面。

<script src="./renderer.js"></script>

总结一下:咱们在 createWindow 的时候把 preload.js 插入到了 html 文件中,经过 contextBridge 设置了大局目标,在烘托进程就能拜访到咱们设置的特别大局特点 versions。

在进程之间通讯(双向)

单向 IPC 音讯从烘托器进程发送到主进程,您能够运用 ipcRenderer.send API 发送音讯,然后运用 ipcMain.on API 接纳。比较简单,请检查官网。

Electron 的主进程和烘托进程有着清楚的分工而且不可互换。 这代表着无论是从烘托进程直接拜访 Node.js 接口,亦或者是从主进程拜访 HTML 文档目标模型 (DOM),都是不或许的。

解决这一问题的办法是运用进程间通讯 (IPC)。能够运用 Electron 的 ipcMain 模块和 ipcRenderer 模块来进行进程间通讯。 为了从你的网页向主进程发送音讯,你能够运用 ipcMain.handle 设置一个主进程处理程序(handler),然后在预处理脚本中露出一个被称为 ipcRenderer.invoke 的函数来触发该处理程序(handler)。

咱们将向烘托器添加一个叫做 ping() 的大局函数,该函数回来一个字符串 ‘pong’。

修改 preload.js 文件:

// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
  ping: () => ipcRenderer.invoke('ping') // 新增一个大局办法 versions.ping()
  // 除函数之外,咱们也能够露出变量
})

在主进程中设置 handle 监听器:

// index.js
const { app, BrowserWindow, ipcMain } = require('electron/main')
const path = require('node:path')
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })
  win.loadFile('index.html')
}
app.whenReady().then(() => {
    // 监听烘托进程触发的办法,并回来一个字符串
  ipcMain.handle('ping', () => 'pong')
  createWindow()
})

在 renderer.js 中新增下面的内容:

// renderer.js
const func = async () => {
    // 调用 'ping' 办法,并等候回来值
  const response = await window.versions.ping()
  console.log(response) // 打印 'pong'
}
func()

咱们已经知道如何运用进程间的通讯,如此一来就能在烘托进程,点击按钮触发主进程的相关操作。依据官网事例,添加事例杂乱程度,设计一个能够切换白日/黑暗形式的桌面软件

electron 入门教程

代码仓库

主进程到烘托器进程

  1. 运用 webContents 模块发送音讯
// index.js 省掉部分代码
const createWindow = () => {
    const win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
    })
    // 运用 webContents.send() 来向烘托进程发送音讯
    const menu = Menu.buildFromTemplate([
        {
            label: app.name,
            submenu: [
                {
                    click: () => win.webContents.send('update-counter', 1),
                    label: 'Increment'
                },
                {
                    click: () => win.webContents.send('update-counter', -1),
                    label: 'Decrement'
                }
            ]
        }
    ])
    Menu.setApplicationMenu(menu)
    win.loadFile('index.html')
    // 翻开开发东西
    win.webContents.openDevTools()
}
  1. 经过预加载脚本露出 ipcRenderer.on
// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
    onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value)),
})
  1. 在烘托页面监听事情
// renderer.js 省掉部分代码
const counter = document.getElementById('counter')
window.electronAPI.onUpdateCounter((value) => {
  const oldValue = Number(counter.innerText)
  const newValue = oldValue + value
  counter.innerText = newValue.toString()
})
  1. 可选:回来一个回复

关于从主进程到烘托器进程的 IPC,没有与 ipcRenderer.invoke 等效的 API。 不过,您能够从 ipcRenderer.on 回调中将回复发送回主进程。

// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
    onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value)),
    // 给主进程发送一个 counter-value 事情
    counterValue: (value) => ipcRenderer.send('counter-value', value)
})

在主进程中,监听 counter-value 事情并适当地处理它们。

ipcMain.on('counter-value', (_event, value) => {
  console.log(value) // will print value to Node console
})

完结官方的事例,实现一个客户端菜单操作。

代码仓库同上

烘托进程之间进行通讯

没有直接的办法能够运用 ipcMain 和 ipcRenderer 模块在 Electron 中的烘托器进程之间发送音讯。 为此,您有两种挑选:

  • 将主进程作为烘托器之间的音讯代理。 这需要将音讯从一个烘托器发送到主进程,然后主进程将音讯转发到另一个烘托器。
  • 从主进程将一个 MessagePort 传递到两个烘托器。 这将允许在初始设置后烘托器之间直接进行通讯。

第一种方法,相当于把主进程当作中心的桥梁,交互比较杂乱。直接上代码 烘托进程之前进行通讯演示

第二种方法,经过 MessageChannelMain 树立2个通道,在经过 webContents.postMessage 在主进程把对应的通道发送到烘托进程。preload.js 中咱们能够监听 ‘port’ 事情,经过 window.postMessage 将 port 端口发送到同源的页面上。相同的,同源的页面也能够用 window.onmessage 监听发送来的数据。

// index.js 
// 省掉部分代码
// 创立窗口
const mainWindow = new BrowserWindow({
    webPreferences: {
        contextIsolation: true, // Electron 12.0.0 及以上版别默认启用
        preload: path.join(__dirname, 'preloadMain.js')
    }
})
mainWindow.loadFile('index.html')
mainWindow.webContents.openDevTools()
const secondaryWindow = new BrowserWindow({
    webPreferences: {
        preload: path.join(__dirname, 'preloadSecondary.js')
    }
})
secondaryWindow.loadFile('second.html')
secondaryWindow.webContents.openDevTools()
// 树立通道
const { port1, port2 } = new MessageChannelMain()
// webContents准备就绪后,运用postMessage向每个webContents发送一个端口。
mainWindow.once('ready-to-show', () => {
    // 通知烘托进程,这里有一个 port 办法,能够与别的一个窗口通讯
    mainWindow.webContents.postMessage('port', null, [port1])
})
secondaryWindow.once('ready-to-show', () => {
    secondaryWindow.webContents.postMessage('port', null, [port2])
})
// preload.js
const { ipcRenderer } = require('electron')
const windowLoaded =  new Promise((relose)=> {
    window.onload = relose
})
ipcRenderer.on('port', async (event) => {
    await windowLoaded // 等候页面加载完结
    window.postMessage('channel-port', '*', event.ports)
})
<!--烘托进程页面-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Main Page</h1>
    <script>
        window.onmessage = (event) => {
            if (event.source === window && event.data === 'channel-port') {
                const [port] = event.ports;
                port.onmessage = (event) => { // 打印接纳到的音讯
                    console.log(event.data);
                }
                port.postMessage('i am main page'); // 测试发送音讯
            }
        }
    </script>
</body>
</html>

完整代码,请检查 烘托进程之间直接通讯

运用 Electron Forge 打包

Electron 的中心模块中没有捆绑任何用于打包或分发文件的东西。 假如您在开发形式下完结了一个 Electron 运用,需要运用额外的东西来打包运用程序 (也称为可分发文件) 并分发给用户 。 可分发文件能够是装置程序 (例如 Windows 上的 MSI) 或者绿色软件 (例如 macOS 上的 .app 文件)。

Electron Forge 是一个处理 Electron 运用程序打包与分发的一体化东西。 在东西底层,它将许多现有的 Electron 东西 (例如 @electron/packager、 @electron/osx-sign、electron-winstaller 等) 组合到一同,因此您不必费心处理不同系统的打包工作。

装置依靠

npm install --save-dev @electron-forge/cli @electron/fuses @electron-forge/plugin-fuses
npx electron-forge import

转换脚本完结后,Forge 会将一些脚本添加到您的 package.json 文件中。

{
  "scripts": {
    "start": "electron-forge start",
    "package": "electron-forge package",
    "make": "electron-forge make"
  }
}

要创立可分发文件,请运用项目中的 make 脚本,该脚本最终运转了 electron-forge make 指令。

npm run make

该 make 指令包括两步:

它将首要运转 electron-forge package ,把您的运用程序 代码与 Electron 二进制包结合起来。 完结打包的代码将会被生成到一个特定的文件夹中。 然后它将运用这个文件夹为每个 maker 装备生成一个可分发文件。 在脚本运转后,您应该看到一个 out 文件夹,其中包括可分发文件与一个包括其源码的文件夹。

最终一步,咱们需要对代码进行签名,请检查其他资料。