Electron
简介
Electron 是一个可以用 JavaScript、HTML 和 CSS 构建桌面应用程序的库。这些应用程序能打包到 Mac、Windows 和 Linux 系统上运行,也能上架到 Mac 和 Windows 的 App Store。 意思就是说,你只要拥有前端开发的能力,也可以轻松开发跨平台的桌面应用.
Electron 有两种进程:主进程和渲染进程。部分模块只能在两者之一上运行,而有些则无限制。主进程更多地充当幕后角色,而渲染进程则是应用程序的各个窗口。
主进程
主进程,通常是一个命名为 main.js 的文件,该文件是每个 Electron 应用的入口。一个 Electron 应用只能存在一个主进程。它控制了应用的生命周期(从打开到关闭)。它既能调用原生元素,也能创建新的(多个)渲染进程。另外,Node API 是内置其中的。
渲染进程(renderer)
渲染进程是应用的一个浏览器窗口。与主进程不同,它能存在多个并且相互独立(它也能是隐藏的)。
主窗口通常被命名为 index.html。它们就像典型的 HTML 文件,但 Electron 赋予了它们完整的 Node API。因此,这也是它与浏览器的区别。
进程之间的通讯 (IPC Inter-process communication)
想要在网页里调用主进程的功能,比如关闭窗口,最小化全屏等主线程才能控制的功能. Electron提供了通讯的机制,这就是IPC.
Hello World
-
首先创建一个文件夹并初始化 npm 包。
1 2
mkdir my-electron-app && cd my-electron-app npm init
- 将
entry point改为main.js. -
然后,将 electron 包安装到应用的开发依赖中。
1
npm install --save-dev electron
-
在package.json配置文件中的scripts字段下增加一条start命令:
1 2 3 4 5
{ "scripts": { "start": "electron ." } } - 在项目的根目录下创建一个名为main.js的空文件。
-
在您的项目根目录下创建一个名为index.html的文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <!-- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> <title>你好!</title> </head> <body> <h1>你好!</h1> 我们正在使用 Node.js <span id="node-version"></span>, Chromium <span id="chrome-version"></span>, 和 Electron <span id="electron-version"></span>. </body> </html> -
现在我们有了一个html页面,如果想将它加载进应用窗口中,需要两个Electron模块:
app模块,它控制应用程序的事件生命周期。BrowserWindow模块,它创建和管理应用程序 窗口。
在 main.js 文件头部将它们导入作为 CommonJS 模块:
1
const { app, BrowserWindow } = require('electron') -
添加一个createWindow()方法来将index.html加载进一个新的BrowserWindow实例。
1 2 3 4 5 6 7 8
const createWindow = () => { const win = new BrowserWindow({ width: 800, height: 600 }) win.loadFile('index.html') } -
调用createWindow()函数来打开您的窗口。在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口。 您可以通过使用 app.whenReady() API来监听此事件。 在whenReady()成功后调用createWindow()。
1 2 3
app.whenReady().then(() => { createWindow() }) -
管理窗口的生命周期
虽然你现在可以打开一个浏览器窗口,但你还需要一些额外的模板代码使其看起来更像是各平台原生的。 应用程序窗口在每个OS下有不同的行为,Electron将在app中实现这些约定的责任交给开发者们。
在Windows和Linux上,关闭所有窗口通常会完全退出一个应用程序。
为了实现这一点,你需要监听 app 模块的 ‘window-all-closed’ 事件。如果用户不是在 macOS(darwin) 上运行程序,则调用 app.quit()。1 2 3
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() }) -
通过预加载脚本从渲染器访问Node.js
创建一个名为 preload.js 的新脚本如下:1 2 3 4 5 6 7 8 9 10
window.addEventListener('DOMContentLoaded', () => { const replaceText = (selector, text) => { const element = document.getElementById(selector) if (element) element.innerText = text } for (const dependency of ['chrome', 'node', 'electron']) { replaceText(`${dependency}-version`, process.versions[dependency]) } }) -
修改main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
const { app, BrowserWindow } = require('electron') // include the Node.js 'path' module at the top of your file const path = require('node:path') // modify your existing createWindow() function const createWindow = () => { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js') } }) win.loadFile('index.html') } // ...
打包
可以使用以下方式进行打包:
electron-forge
electron-builder
electron-packager
Preload
预加载(preload)脚本包含了那些执行于渲染器进程中,且先于网页内容开始加载的代码 。预加载脚本可以在 BrowserWindow 构造方法中的 webPreferences 选项里被附加到主进程。
1
2
3
4
5
6
7
8
const { BrowserWindow } = require('electron')
// ...
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js'
}
})
// ...
因为预加载脚本与浏览器共享同一个全局 Window 接口,并且可以访问 Node.js API,所以它通过在全局 window 中暴露任意 API 来增强渲染器,以便你的网页内容使用。
我们使用 contextBridge 模块来安全地实现交互:
preload.js
1
2
3
4
5
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {
desktop: true
})
renderer.js
1
2
console.log(window.myAPI)
// => { desktop: true }
electron-forge
最快捷的打包方式是使用 Electron Forge。
- 在package.json中添加description,不然Build会失败
-
添加 Electron Forge 依赖
1 2
npm install --save-dev @electron-forge/cli npx electron-forge import
-
使用 Forge 的 make 命令来创建可分发的应用程序:
1
npm run make
Electron-forge 会创建 out 文件夹
electron-builder
待添加
在Electron中使用Angular
-
安装最新angular-cli
1
npm i -g @angular/cli
-
生成一个angular工程
1
ng new first-angular-electron-app
-
安装最新版electron
1 2
cd first-angular-electron-app npm install --save-dev electron@latest
-
在项目根目录添加主进程 main.js文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
const { app, BrowserWindow , ipcMain } = require('electron'); const path = require('path') const electron = require('electron') let win; const Menu = electron.Menu function createWindow () { // Hide menu Menu.setApplicationMenu(null) // 创建浏览器窗口。 win = new BrowserWindow({ width: 1000, height: 670 , }); // 然后加载应用的 index.html。 win.loadURL(`file://${__dirname}/dist/first-angular-electron-app/browser/index.html`); // 打开开发者工具 win.webContents.openDevTools(); // 当 window 被关闭,这个事件会被触发。 win.on('closed', () => { // 取消引用 window 对象,如果你的应用支持多窗口的话, // 通常会把多个 window 对象存放在一个数组里面, // 与此同时,你应该删除相应的元素。 win = null; }); } // Electron 会在初始化后并准备 // 创建浏览器窗口时,调用这个函数。 // 部分 API 在 ready 事件触发后才能使用。 app.on('ready', createWindow); // 当全部窗口关闭时退出。 app.on('window-all-closed', () => { // 在 macOS 上,除非用户用 Cmd + Q 确定地退出, // 否则绝大部分应用及其菜单栏会保持激活。 if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { // 在macOS上,当单击dock图标并且没有其他窗口打开时, // 通常在应用程序中重新创建一个窗口。 if (win === null) { createWindow(); } }); // 在这个文件中,你可以续写应用剩下主进程代码。 // 也可以拆分成几个文件,然后用 require 导入。 // ipc通讯相关代码 // 登录窗口最小化 ipcMain.on('window-min', function() { win.minimize(); }); // 登录窗口最大化 ipcMain.on('window-max', function() { if (win.isMaximized()) { win.restore(); } else { win.maximize(); } }); ipcMain.on('window-close', function() { win.close(); }); -
package.json 文件修改
添加main.js的位置与npm scripts electron命令1 2 3 4 5 6
{ "scripts": { "electron": "ng build && electron .", }, "main": "main.js", } -
修改src/index.html
1 2 3
<base href="/"> 改为 <base href="./">
-
运行命令行
1
npm run electron
Angular-electron
Angular-electron 这是一个结合 Angular 和 Electron 的项目。以此为基准环境,不需要从头构建。
使用
将代码库克隆到本地:
1
git clone https://github.com/maximegris/angular-electron.git
使用 npm 安装依赖项
1
npm install
使用 npm 安装 NodeJS 依赖项(由 Electron 主进程使用):
1
2
cd app/
npm install
angular-electron使用了两个package.json, 遵循 Electron Builder Two package.json Structure,以优化最终的 bundle 并且仍然能够使用 Angular 功能
编译运行
1
npm start
应用程序代码由app/main.ts管理。在运行npm start时,会同时在http://localhost:4200和 Electron 窗口运行
如果只想运行浏览器,使用以下命令
1
npm run ng:serve
| 命令 | 解释 |
|---|---|
| npm run ng:serve | 在 Web 浏览器中执行应用(dev mode) |
| npm run web:build | build可直接在 Web 浏览器中使用的应用。生成的文件位于 /dist 文件夹中 |
| npm run electron:local | build应用程序并启动 electron |
| npm run electron:build | 基于当前操作系统build应用程序 |
项目结构
| 文件夹 | 解释 |
|---|---|
| app | Electron 主进程文件夹 |
| src | Electron渲染器进程文件夹(Web/Angular) |
导入第三方库
由于项目在两种模式(Web 和 Electron)下运行。因此必须以正确的方式导入依赖项。
有两种第三方库:
- 使用 NodeJS 核心模块(crypto、fs、util…)。建议在两个package.json中都添加这个第三方库,以便使其在 Electron 的主进程(app folder)和 Electron 的渲染器进程(src folder)中都能工作。
- Web相关(比如 bootstrap、material…)
必须添加到 root文件夹下package.json的dependencies
如果添加依赖时需要用到ng-add,则可能会更麻烦。因为项目无法使用默认的@angular-builders。比如,使用Angular-Material
添加Angular-Material
-
需要临时修改angular.json, 经编译器改为Angular默认的编译器
@angular-builders/custom-webpack:browser => @angular-devkit/build-angular:browser
@angular-builders/custom-webpack:dev-server => @angular-devkit/build-angular:dev-server -
使用命令行添加 Angular material
1
ng add @angular/material
-
将自定义builder放回angular.json
@angular-devkit/build-angular:browser => @angular-builders/custom-webpack:browser
@angular-devkit/build-angular:dev-server => @angular-builders/custom-webpack:dev-server
调试
https://www.electronjs.org/zh/docs/latest/tutorial/tutorial-first-app#%E5%8F%AF%E9%80%89%E4%BD%BF%E7%94%A8-vs-code-%E8%B0%83%E8%AF%95
Reference
Quick Start
Angular for Desktop Applications with Electron
Creating a Desktop App with Electron and Angular
angular-electron
从零搭建 Angular7 + Electron 桌面应用
Two package.json Structure
electron-builder