Electron学习
介绍
Electron 可以让我们使用纯 JavaScript 调用丰富的原生 API 来创建桌面应用;可以把它看作是专注于桌面应用而不是 web 服务器的 io.js 的一个变体。
当然这不意味着 Electron 是绑定了 GUI 库的 JavaScript;相反,Electron 使用 web 页面作为它的 GUI,所以可以把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器——从这个角度来理解,我们就明白了,所有和系统的交互功能,Electron 这个平台已经搞定了,我们所需要的就是创建一个自己喜欢的界面,一个或多个核心的功能,以及调用它提供给我们的 API 就好。
因为 Electron 本身其实也是 Node.js 的一个第三方项目,所以在开发的时候,我们首先需要已经安装了 Node.js,以及包管理器 NPM,外加一个顺手的编辑器:我个人用 VS Code 很顺手,当然如果喜欢用别的编辑器也没问题,比如前文提到的 Notepad++,或者著名的 Sublime Text、Atom(话说这个也是 Electron 做的)、Komodo Edit……等等等等,都可以。
接下来,用 NPM 在本地安装一个 Electron 的副本即可,可以选择全局安装或者针对项目单独安装:
npm i -g electron
一个基础的 Electron 项目结果如下:
my-electron-app/
├── package.json
├── main.js
└── index.html
因为 Node.js 对项目结构以及命名还是有点要求的,所以我个人推荐用一些 Electron Cli 工具来进行项目启动和初始化,这样会相对省一些事,比如 electron-forge
Electron Forge
是创建、发布和安装现代Electron 应用程序的完整工具。
安装
建议是安装成全局,这样方便使用npm install -g electron-forge
使用
新建一个文件夹,定位到文件夹。
1、在你的应用程序中安装 Electron 作为开发依赖
npm install electron --save-dev
【--save-dev 的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖。】
--save-dev 与--save的区别
npm install 在安装 npm 包时,有两种命令参数可以把它们的信息写入 package.json 文件,一个是npm install--save另一个是 npm install –save-dev,他们表面上的区别是--save 会把依赖包名称添加到 package.json 文件 dependencies 键下,--save-dev 则添加到 package.json 文件 devDependencies 键下
- --save-dev,会在devDependencies里面添加依赖
- -D,会在devDependencies里面添加依赖
- --save,会在dependencies里面添加依赖
- -S,会在dependencies里面添加依赖
2、通过 init 命令指定当前文件夹为新项目:
electron-forge init
【这里要留意,如果 NPM 的映像是指向淘宝的话,整个过程会快很多。查看镜像:npm config get registry】
完成后,初始配置就结束了:
3、这个初始代码已经可以运行了,只要运行如下命令:electron-forge start
可以看到,初始的界面是包括了菜单栏、主界面以及控制台的,而控制台的出现就分明体现了它本质上是个浏览器的特点,这也使得我们在本地及时的进行调整、修复和新功能测试变得非常简单。
更多参考:快速了解Electron:新一代基于Web的跨平台桌面技术
快速入门
通过使用Electron创建一个极简的 Hello World 应用,该应用与electron/electron-quick-start
类似
该应用将会打开一个浏览器窗口,来展示包含当前正在运行的 Chromium, Node.js, and Electronweb等版本信息的web界面
前提条件:node.js
注意:因为 Electron 将 Node.js 嵌入到其二进制文件中,你应用运行时的 Node.js 版本与你系统中运行的 Node.js 版本无关。
创建应用程序
Electron 应用程序遵循与其他 Node.js 项目相同的结构。
1、首先创建一个文件夹并初始化 npm 包。
mkdir my-electron-app && cd my-electron-app
npm init
init
初始化命令会提示您在项目初始化配置中设置一些值:
2、然后,将 electron
包安装到应用的开发依赖中。[注save前是两个-]
$ npm install --save-dev electron
3、最后,您希望能够执行 Electron 如下所示,在您的 package.json
配置文件中的scripts
字段下增加一条start
命令:"start": "electron ."
最终你的package.json如下:
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "\"hello world\"",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "electron ."
},
"author": "huy",
"license": "ISC",
"devDependencies": {
"electron": "^13.1.3"
}
}
4、start
命令能让您在开发模式下打开您的应用,即 : npm start
注意:此时运行程序会报错,因为还没有入口文件。
运行主进程
任何 Electron 应用程序的入口都是 main
文件。 这个文件控制了主进程,它运行在一个完整的Node.js环境中,负责控制您应用的生命周期,显示原生界面,执行特殊操作并管理渲染器进程(稍后详细介绍)。
要初始化这个main
文件,需要在您项目的根目录下创建一个名为main.js
的空文件。
注意:此时运行程序不会报错,但是没有反应,因为还没有页面。
创建页面
在可以为我们的应用创建窗口前,我们需要先创建加载进该窗口的内容。 在 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 World!</title> </head> <body> <h1>Hello World!</h1> We are using Node.js <span id="node-version"></span>, Chromium <span id="chrome-version"></span>, and Electron <span id="electron-version"></span>. </body> </html>
在窗口中打开您的页面
现在您有了一个页面,将它加载进应用窗口中。 要做到这一点,你需要 两个Electron模块:
app
模块,它控制应用程序的事件生命周期。BrowserWindow
模块,它创建和管理应用程序 窗口。
因为主进程运行着Node.js,您可以在文件头部将他们导入作为 公共JS模块:
// Modules to control application life and create native browser window const {app , BrowserWindow } =require('electron') //添加一个createWindow()方法来将index.html加载进一个新的BrowserWindow实例。 function createWindow () { const win = new BrowserWindow({ width: 800, height: 600 }) win.loadFile('index.html') } //调用createWindow()函数来打开您的窗口。 //只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口。 通过使用 app.whenReady() API来监听此事件。 在whenReady()成功后调用createWindow()。 app.whenReady().then(()=>{ createWindow() })
此时,执行 npm start
通过预加载脚本从渲染器访问Node.js
现在要做的是输出Electron的版本号和它的依赖项到你的web页面上。
在主进程通过Node的全局 process
对象访问这个信息是微不足道的。 然而,你不能直接在主进程中编辑DOM,因为它无法访问渲染器 文档
上下文。 它们存在于完全不同的进程!
这是将 预加载 脚本连接到渲染器时派上用场的地方。 预加载脚本在渲染器进程加载之前加载,并有权访问两个 渲染器全局 (例如 window
和 document
) 和 Node.js 环境。
创建一个名为 preload.js
的新脚本如下:
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]) } })
要将此脚本附加到渲染器流程,请在你现有的 BrowserWindow
构造器中将路径中的预加载脚本传入 webPreferences.preload
选项
const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js') } })
这里使用了两个Node.js概念:
我们使用一个相对当前正在执行JavaScript文件的路径,这样您的相对路径将在开发模式和打包模式中都将有效。
此时,执行 npm start,结果如下:
总结
1、我们引导了一个Node.js应用程序,并添加了Electron作为依赖项。
2、我们创建了一个main.js脚本来运行我们的主进程,它控制我们的应用程序并在Node.js环境中运行。在这个脚本中,我们使用Electron的app和BrowserWindow模块来创建一个浏览器窗口,该窗口在一个单独的进程(渲染器)中显示web内容。
3、为了访问渲染器中的某些Node.js功能,我们在BrowserWindow构造函数中附加了一个预加载脚本。
打包
推荐使用cli(electron-forge)
建议是安装成全局,这样方便使用npm install -g electron-forge
这里有两种形式的打包,分别在不同场景下使用
-
electron-forge package
只是打包成一个目录到out目录下,注意这种打包一般用于调试,并不是用于分发 -
electron-forge make
这个才会打出真正的分发包,放在out\make目录下
两个命令都可以使用--arch 和--platform参数来指定系统结构和平台,但是需要注意的是只能打包你当前机器的平台包,比如你用OSX是无法打出windows平台安装包的;这两个参数不填写的话,默认和当前系统一致
另外,make是用squirrel打出来的包,安装后是放在%localappdata%下的
官方
其他学习
主脚本文件main.js/index.js
1、调用模块
引用 Electron 模块:
import { app, BrowserWindow } from 'electron';
或者用更符合 ES 6 的方式来引入:
const { app, BrowserWindow } = require('electron');
2、应用退出
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
}
当应用更新、卸除的时候,应用会自动退出就好。
3、主窗口
- 指定主窗口的内容从哪里加载,这里指定了 src 目录下的 index.html:
mainWindow.loadURL(`file://${__dirname}/index.html`);
这里使用了 Node.js 的路径通配符 ${__dirname},通常就是当前文件所在路径的名称,
- 是否允许开发工具可见:【程序运行没有效果时,可以开启看看是什么问题】
mainWindow.webContents.openDevTools();
在前期的开发工作当中,开发者工具是我们很重要的辅助工具,一般都是需要打开的;只有在需要对外发布的时候,我们才需要去除这一行使得开发工具“隐身”。
4、应用加载
就是 Electron 已经完成了加载窗口前的准备,应用在各个不同状态时,需要启动其他 API 的时候一个应用处理的机制,本质上就是侦听器:app.on();
Electron的进程
Electron 是由两种进程组成的,即主进程和渲染进程:
- 在 Electron 里,运行 package.json 里 main 脚本的进程被称为主进程(Main Process),在主进程运行的脚本可以以创建 web 页面的形式展示 GUI:
- 主进程通过创建浏览器窗口实例来创建网页:每一个浏览窗口实例在其渲染过程中运行网页,当一个 BrowserWindow 实例被摧毁时,对应的渲染过程也被终止
- 主进程管理所有网页及其对应的渲染过程
- 由于 Electron 使用 Chromium 来展示页面,所以 Chromium 的多进程结构也被充分利用:每个 Electron 的页面都在运行着自己的进程,这样的进程我们称之为渲染进程(Renderer Process):
- 渲染进程只能管理单个的网页,在一个渲染过程中崩溃不会影响其他渲染过程
- 渲染进程通过 IPC 与主进程通信,在网页上执行 GUI 操作;由于安全考虑和可能的资源泄漏,直接从渲染器过程中调用与本地 GUI 有关的 API 会受到限制
- 在html中引入渲染进程代码:<script src="./renderer.js"></script>
两者之间的通信可以通过进程间通信模块进行:ipcMain 和 ipcRenderer ;还有一个从渲染进程调用主进程模块的通讯模块:remote。
进程API
针对不同的进程,Electron 提供了不同的 API 以供使用,所以可以分为以下3类 API:
- 主进程 API
- 渲染进程API
- 通用API
进程间通信
1、从渲染进程到主进程
Callback 写法:
• ipcRenderer.send
• ipcMain.on
Promise 写法 (Electron 7.0 之后,处理请求 + 响应模式)
• ipcRenderer.invoke
• ipcMain.handle
2、从主进程到 渲染进程
• ipcRenderer.on
• webContents.send
报错
1、electron渲染进程报require is not defined
webPreferences:{ nodeIntegration:true, contextIsolation:false }
参考:踩坑electron渲染进程renderer,解决require is not defined的问题
2、electron windows通知不生效