Electron从入门到精通
一,Electron 快速入门
1、简介
Electron是利用web前端技术进行桌面应用开发的一套框架,它是由Github开发的,利用HTML、CSS、JavaScript来构建跨平台桌面应用程序的一个开源库。Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包成Mac、Windowns、Linux系统下的应用来实现这一目的。
若想开发一个兼容多平台的桌面应用,以往常用的技术框架有GTK、QT等,这些框架受语言限制,且学习成本较高,效率有限。目前一些基于前端技术的hybrid框架很流行,且已经在多个领域得到了广泛的应用和验证,比如利用前端技术+相应的打包工具可开发适配多平台的应用(PC、微信公众号、小程序、Android、IOS等)。Electron就是这样一款框架,为前端技术人员利用web前端技术开发桌面应用带来了可能,开发人员可利用已经掌握的前端技术如Html、CSS、JavaScript,以及结合一些前端技术框架:Vue、Angular、React、webpack,加之浏览器渲染引擎、Electron封装的系统API快速实现一款桌面应用的开发,Electron做了大部分复杂的工作,开发人员只需要专注在核心业务和前端技术本身。同时,通过一定的优化,Electron可以做到很好的体验。
目前有不少知名桌面应用采用Electron开发,如:开发人员熟知的Visual Studio Code、MongoDB桌面版管理工具、Skype桌面版、WhatsApp桌面版、HTTP网络测试工具Postman等。
2、组成
Chromium
Chromium为Electron提供了强大的UI能力,可以不考虑兼容性的情况下,利用强大的Web生态来开发界面。本质上就是chromium(chrome开源版本)浏览器,有最新的东西都会在chromium测试,所以electron可以体验最新的api,这也是好处之一。其多进程架构图如下:
简单描述其原理:
主进程中的RenderProcessHost和 render 进程中的RenderProcess是用来处理进程间通信的(IPC(Inter-Process Communication,进程间通信)。
Render 进程中的 RenderView 内容基于 WebKit(浏览器引擎) 排版展示出来的
Render 进程中的ResourceDispatcher是用来处理资源请求的。Render 进程中如果有请求则创建一个请 求 ID,转发到 IPC,由 Browser 进程中处理后返回。
Chromium 是多进程架构,包括一个主进程,多个渲染进程。
Node.js
Node.js是让Electron有了底层的操作能力,比如文件的读写,甚至是集成C++等等操作,并可以使用大量开源的npm
包来完成开发需求
Native API
Native API让Electron有了跨平台和桌面端的原生能力,比如说它有统一的原生界面,窗口、托盘、消息通知这些。
3、涉及前端技术
Electron是基于Chromium和Node.js实现的,所以开发人员所需要使用到的前端技术主要包括以下方面:
(1)、Html、CSS、JavaScript、ES6
(2)、前端开发工具Vue、Angular、React等的一种
(3)、其他网络、缓存、通讯等前端技术
4、架构
Electron 架构和 Chromium 架构类似,也是具有1个主进程和多个渲染进程。但是也有区别:
(1)、在各个进行中暴露了 Native API ,提供了 Native 能力
(2)、引入了 Node.js,所以可以使用 Node 的能力
(3)、但是渲染进程使用node 需要配置,下文会有所提到
可以简单的理解为Electron为web项目套上了Node.js环境的壳,使得我们可以调用Node.js的丰富的API。这样我们可以用JavaScript来写桌面应用,拓展很多我们在web端不能做的事情。
Electron的结构示意图如下:electron核心可以分成2个部分即:主进程和渲染进程。
主进程
Electron 运行 package.json 的 main 脚本的进程被称为主进程 (只有一个)。
主进程特点:
主进程连接着操作系统和渲染进程,可以把她看做页面和计算机沟通的桥梁
进程间通信、窗口管理
全局通用服务
一些只能或适合在主进程做的事情,如浏览器下载、全局快捷键处理、托盘、session
维护一些必要的全局状态
渲染进程
渲染进程就是我们所熟悉前端环境了。只是载体改变了,从浏览器变成了window。
注:出于安全考虑,渲染进程是不能直接访问本地资源的),因此都需要在主进程完成。
渲染进程特点:
Electron 使用了 Chromium 来展示 web 页面,所以 Chromium 多进程架构也被使用到。
每个web页面运行在自己的进程中。每个渲染进程是相互独立的且只关心自己网页。
使用BrowserWindow类开启一个渲染进程并将这个实例运行在该进程中,当一个被销毁后,相应的渲染进程也会被终止。
渲染进程不能调用原生资源,但同样包含Node.js环境,所以可以引入Node.js。
主进程与渲染进程的区别
主进程使用 BrowserWindow 实例创建网页。
每个 BrowserWindow 实例都在自己的渲染进程里运行着一个网页。当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止。
主进程管理所有页面和与之对应的渲染进程。
由于在网页里管理原生 GUI 资源是非常危险而且容易造成资源泄露,所以在网页面调用 GUI 相关的 APIs 是不被允许的。如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。
二,Electron 桌面环境搭建
1、安装Node.js
(1)、默认安装,一直next
(2)、验证node是否安装成功
打开cmd窗口,在命令窗口输入命令:node -v ,如果出现版本号,即表示安装成功
还可以查看npm的版本,在cmd命令窗口中输入命令:npm -v
2、安装Electron
(1)、安装cnpm,采用淘宝镜像安装electron
打开cmd窗口,在命令窗口输入命令下载安装:
npm install cnpm -g --registry=https://registry.npm.taobao.org
在cmd命令窗口中输入命令:cnpm -v 查看版本信息
(2)、安装electron,全局安装
打开cmd窗口,在命令窗口输入命令下载安装:cnpm install electron -g
在cmd命令窗口中输入命令:electron -v 查看版本信息
3、测试项目
(1)、使用WebStorm新建空项目
(2)、在根目录下新建一个html文件,文件名称为helloworld.html
(3)、在根目录下新建一个 main.js 文件,并填写如下代码,这个就是 Electron 的主进程文件
// 1、引入electron模块
let electron = require('electron');
// 2、创建electron引用
let app = electron.app;
// 3、创建窗口引用
let BrowserWindow = electron.BrowserWindow;
// 4、声明要打开的主窗口
let mainWindow = null;
app.on('ready',() => {
// 设置窗口大小
mainWindow = new BrowserWindow({width:300, height:200});
// 加载哪个页面
mainWindow.loadFile('helloworld.html');
// 监听关闭事件,把主窗口设置为null,否则内存占用越来越多
mainWindow.on('closed',() => {
mainWindow = null;
})
})
(4)、在终端位置直接使用命令:npm init --yes 来初始化 package.json 文件
文件生成后如下:
{
"name": "electron",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
(5)、运行项目,在终端位置输入命令:electron . (注意命令最后带 .)
三,Electron 项目实战
1、Electron remote模块的使用
Electron 的 API 方法和模块也是分为可以在主进程和渲染进程中使用。那如果我们想在渲染进程中使用主进程中的模块方法时,可以使用 Electro remote 来解决在渲染和主进程间的通讯。这节我们就实现一个通过Web中的按钮打开新窗口。
(1)、在项目根目录下,新建一个 demo1.html 文件,然后快速生成html的基本结构,编写一个按钮,引入渲染的js页面。代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo1</title>
</head>
<body>
<button id="btn">打开新的窗口</button>
<script src="render/demo1.js"></script>
</body>
</html>
(2)、在render文件夹下,新建一个 demo3.js 文件,然后编写如下代码:
// 1、获取ID为btn的选择器
const btn = this.document.querySelector('#btn');
// 2、创建窗口引用
const BrowserWindow = require('electron').remote.BrowserWindow;
// 3、打开窗口
window.onload = function (){
btn.onclick = () => {
newWin = new BrowserWindow({width: 300, height: 200})
newWin.loadFile('test1.html')
newWin.on('close', () => {newWin = null})
}
}
(3)、在项目根目录下创建test1页面,然后写入下面的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test1</title>
</head>
<body>
<h1>hello World!</h1>
</body>
</html>
(4)、 修改 main.js ,增加全量使用node.js,内容:
// 1、引入electron模块
let electron = require('electron');
// 2、创建electron引用
let app = electron.app;
// 3、创建窗口引用
let BrowserWindow = electron.BrowserWindow;
// 4、声明要打开的主窗口
let mainWindow = null;
app.on('ready',() => {
// 设置窗口大小
mainWindow = new BrowserWindow({
width:300,
height:200,
// 增加全量使用node.js,因为我们要使用node里的fs模块
// enableRemoteModule 设置为true,否则不允许在渲染器上使用 remote
webPreferences: { nodeIntegration: true, enableRemoteModule: true}
});
// 加载哪个页面
mainWindow.loadFile('demo1.html');
// 监听关闭事件,把主窗口设置为null,否则内存占用越来越多
mainWindow.on('closed',() => {
mainWindow = null;
})
})
(5)、在终端中运行 electron . 命令,查看测试效果
2、Electron创建菜单和基本使用
(1)、在项目的根目录下新建一个main文件夹,用于存放主进程中用到的代码,在文件夹中新建文件menu.js,内容如下:
const { Menu, BrowserWindow } = require('electron')
// 菜单模板
let template = [
{
label: '前端技能',
submenu: [
{
label: 'JavaScript',
// 加入click事件
click: () => {
win = new BrowserWindow({
width: 200,
height: 200,
webPreferences: { nodeIntegration: true }
})
win.loadFile('test2.html')
win.on('closed', () => {
win = null
})
}
},
{ label: 'TypeScript' }
]
},
{
label: '后端技能',
submenu: [
{ label: 'golang' },
{ label: 'Python' }
]
}
]
// 设置菜单
let m = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(m)
(2)、项目根目录新建测试文件 test2.html,内容如下:
<!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>hello World!</h1>
</body>
</html>
(3)、项目根目录新建测试文件 demo2.html,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo2</title>
</head>
<body>
</body>
</html>
(4)、修改 main.js ,并在ready生命周期中,引入自定义菜单文件 require('./main/menu.js');
// 1、引入electron模块
let electron = require('electron');
// 2、创建electron引用
let app = electron.app;
// 3、创建窗口引用
let BrowserWindow = electron.BrowserWindow;
// 4、声明要打开的主窗口
let mainWindow = null;
app.on('ready',() => {
// 自定义菜单
require('./main/menu.js');
// 设置窗口大小
mainWindow = new BrowserWindow({
width:300,
height:200,
// 增加全量使用node.js,因为我们要使用node里的fs模块
// enableRemoteModule 设置为true,否则不允许在渲染器上使用 remote
webPreferences: { nodeIntegration: true, enableRemoteModule: true}
});
// 加载哪个页面
mainWindow.loadFile('demo2.html');
// 监听关闭事件,把主窗口设置为null,否则内存占用越来越多
mainWindow.on('closed',() => {
mainWindow = null;
})
})
(5)、在终端中运行 electron . 命令,查看测试效果
3、Electron制作右键菜单
(1)、菜单快捷键绑定
接着上面的 demo2 ,绑定快捷键的属性是 accelerator 属性,比如我们要用快捷键新打开一个窗口,我们的 menu.js
代码可以写成这样:accelerator:`ctrl+n`
const { Menu, BrowserWindow } = require('electron')
// 菜单模板
let template = [
{
label: '前端技能',
submenu: [
{
label: 'JavaScript',
// 绑定快捷键
accelerator:`ctrl+n`,
// 加入click事件
click: () => {
win = new BrowserWindow({
width: 200,
height: 200,
webPreferences: { nodeIntegration: true }
})
win.loadFile('test2.html')
win.on('closed', () => {
win = null
})
}
},
{ label: 'TypeScript' }
]
},
{
label: '后端技能',
submenu: [
{ label: 'golang' },
{ label: 'Python' }
]
}
]
// 设置菜单
let m = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(m)
(2)、创建右键菜单
右键菜单的响应事件是写在渲染进程中的,也就是写在根目录下的demo2.html中的,所以要使用,就要用到remote
模块进行操作了,先来看看右键的相应事件,我们打开 render 文件夹,然后打开 demo2.js 文件,编写一个右键菜单的监听事件,代码如下:
window.addEventListener('contextmenu',function () {
alert('您好!');
})
修改demo2.html,增加引入 <script src="render/demo2.js"></script>
当我们要使用 Menu
模块,它是主线中的模块,如果想在渲染线程中使用,就必须使用 remote
。demo2.js 代码如下:
// window.addEventListener('contextmenu',function () {
// alert('您好!');
// })
const { remote } = require('electron')
let rigthTemplate = [
{
label: '粘贴',
accelerator: 'ctrl+v'
},
{
label: '复制',
accelerator: 'ctrl+c'
}
]
let m = remote.Menu.buildFromTemplate(rigthTemplate);
window.addEventListener('contextmenu',function (e) {
// 阻止当前窗口默认事件
e.preventDefault();
// 把菜单模板添加到右键菜单
m.popup({window:remote.getCurrentWindow()});
})
(3)、程序打开调试模式,运行测试
由于我们已经定义了顶部菜单,没有了打开调试模式的菜单了,这时候可以使用程序来进行打开。
在 main.js 主进程的 ready 生命周期中加入如下这句代码就可以了:
mainWindow.webContents.openDevTools()
4、Electron中通过链接打开浏览器
5、Electron中嵌入网页和打开子窗口
6、Electron Window.open子窗口和父窗口间的通信
7、Electron选择文件对话框
8、Electron保存对话框的操作
9、Electron消息对话框的操作
10、Electron断网提醒功能制作
11、Electron底部通知消息的制作
12、Electron注册全局快捷键
13、Electron剪贴板事件的使用
14、Electron-vue开发项目
四,Electron 项目进阶
1、克隆示例项目
(1)、项目仓库地址
git clone https://github.com/electron/electron-quick-start
(2)、下载完成后进入 electron-quick-start 目录中
(3)、使用淘宝镜像下载项目所需的依赖
采用如下命令设置淘宝镜像
npm config set ELECTRON_MIRROR https://npm.taobao.org/mirrors/electron/
采用命令 npm install 进行所需依赖安装
(4)、启动工程测试
采用命令 npm start 启动项目
2、app常用事件
(1)、ready 当electron完成初始化时被触发
app.on('ready',() => {
console.log("*******ready******");
createWindow();
})
(2)、window-all-closed 所有窗口被关闭
app.on('window-all-closed', function () {
console.log("*******window-all-closed******");
})
(3)、quit 在所有应用程序退出时发出
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') {
console.log("*******quit******");
app.quit()
}
})
(4)、open-file 在应用中要打开一个文件时发出
(5)、open-url 应用中要打开一个URL网址时发出
(6)、activate 当应用被激活时发出
3、webContents常用事件
(1)、did-finish-load 导航完成时触发,即选项卡的旋转器将停止旋转,并指派 onload 事件后
mainWindow.webContents.on('did-finish-load',()=>{
console.log("did-finish-load")
})
(2)、dom-ready 一个框架中的文本加载完成后触发该事件
mainWindow.webContents.on('dom-ready',()=>{
console.log("dom-ready")
})
4、process 进程对象
(1)、编辑Index.html文件,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<link href="./styles.css" rel="stylesheet">
<title>Process</title>
</head>
<body>
<button id="buttonProcess" >查看Process信息</button>
<script src="./renderer.js"></script>
</body>
</html>
(2)、编辑渲染进程renderer.js,定义查看进程信息
var processBtn=document.getElementById('buttonProcess');
processBtn.onclick=getProcessInfo;
function getProcessInfo() {
console.log("getCPUUsage=",process.getCPUUsage());
console.log("arch=",process.arch);
console.log("platform=",process.platform);
console.log("env=",process.env);
}
5、File对象
在文件系统中,使用HTML5 File 原生API操作文件
DOM文件接口为原生文件提供了抽象,以便让用户使用HTML5文件API直接处理原生文件对象。Electron已经向文件对象接口添加了一个path属性,在文件系统中暴露出文件的真实路径
(1)、在index.html文件中添加文件拖动代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<link href="./styles.css" rel="stylesheet">
<title>Process</title>
</head>
<body>
<div class="for_file_drag" id="drag_test">
<h2>File对象</h2>
<span>往这里拖动文件</span>
</div>
<button id="buttonProcess" >查看Process信息</button>
<script src="./renderer.js"></script>
</body>
</html>
(2)、在styles.css样式中新增拖拽区域的样式
.for_file_drag {
width: 100%;
height: 200px;
background: pink;
}
(4)、在renderer.js中添加如下方法
const electron = require("electron");
const fs = require('fs');
const dragWrapper = document.getElementById("drag_test");
dragWrapper.addEventListener('drop',(e) => {
e.preventDefault();
const files = e.dataTransfer.files;
if(files && files.length>0){
// 获取文件路径
const path = files[0].path;
console.log('path',path);
// 获取文件内容
const content = fs.readFileSync(path);
console.log(content.toString());
}
})
dragWrapper.addEventListener('dragover',(e)=>{
e.preventDefault();
})
(5)、测试
6、webview
在一个独立的frame和进程里面显示外部web内容
7、window.opn
8、BrowserWindowProxy
9、BrowserWindow
10、BrowserView
11、dialog
12、系统快捷键
13、主进程与渲染进程通信
14、原生应用菜单
15、网络
16、集成VUE
16、E
更多推荐
所有评论(0)