目录
前言
Electron是由GitHub众多开发者开发的一个开源项目,能够使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序。
当然了,它也可以与vue和react相结合。前端的小伙伴们,学习它应该很快,是很容易上手的一门技术。
文档编写不易,还请各位耐心观看。另外本人能力有限,如有不足之处,还请不吝批评指正!
一、安装需知
需要提前安装好Nodejs,因为Electron依赖于Nodejs。
注意二者有版本要求,比如Electron的v25.0.1版本就要求nodejs必须大于等于18.15.0版本。
所以安装之前建议去Electron的官网查看以下对应的版本。附上地址:Electron Releases
二、安装electron
个人推荐版本:
Node:v16.13.2 Electron:v18.1.0
初始化项目
新建一个文件夹,在文件夹内初始化项目
npm init
安装electron
注意安装时指定版本,否则就是安装的最新版本。一旦安装最新版本,倘若你们nodejs不是最新的,就有可能导致无法安装,提示让你下载更高版本的nodejs。
npm install electron@v18.1.0 --save-dev
检查是否安装成功
安装完成后,我们可以通过如下命令检验版本号来判断是否安装成功
npx electron -v
或者我们可以直接进入在根路径下执行如下命令打开程序,如果弹出应用程序,说明安装成功了。
./node_modules/.bin/electron
三、开始
上面出现的这个应用程序界面是electron内置的一个demo样例。
下面我们就正式开发一个属于自己的界面了。
3.1 修改package.json文件
我们项目初始化完成后,默认会生成一个package.json的文件。
我们需要在scripts对象中追加一个执行命令:
"scripts": {
"start": "electron ."
}
当然了,执行命令叫什么自己取都行,但是实际要执行的必须是electron .
其次,我们发现"main": "index.js",但是,我们并没有这个index.js,因此需要自己在根路径下新建该文件。为了符合electron的习惯,我们习惯上将index.js命名为main.js。它就是我们主程序的入口。
所以,修改后的package.json文件如下:
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"electron": "electron ."
},
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^18.1.0"
}
}
3.2 创建main.js文件
main.js作为程序的入口,我们应该怎么写呢?
直接看下面的代码,相关说明我都注释标注了。
// 导入electron对象
// app 控制应用程序的事件生命周期。
// BrowserWindow 创建并控制浏览器窗口。
const { app,BrowserWindow } = require('electron')
// 控制ready事件
// ready事件是:当 Electron 完成初始化时,发出一次。
app.on('ready',()=>{
// 新建窗口
new BrowserWindow({
width: 800,
height: 600
})
})
3.3 启动预览窗口
下面,我们执行启动命令查看一下:
npm run electron
ok,窗口成功创建出来了,我们接下来就是让他显示内容了。
3.4 显示内容
我们创建窗口之后,就可以获得这个窗口的返回对象。
通过这个返回对象,我们可以让它加载一个HTML页面,这就是electron显示内容的方式了。
// 导入electron对象
// app 控制应用程序的事件生命周期。
// BrowserWindow 创建并控制浏览器窗口。
const { app,BrowserWindow } = require('electron')
// 控制ready事件
// ready事件是:当 Electron 完成初始化时,发出一次。
app.on('ready',()=>{
// 新建窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600
});
// 让主窗口加载文件 html文件
mainWindow.loadFile('./src/index.html');
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>欢迎来到electron</h1>
</body>
</html>
重新执行npm run electron启动,发现就成功的读取到了我们编写的index.html页面了。
四、 热加载
通过上面的案例我们发现,每次编写完代码都需要重新运行。这样就比较麻烦了,有没有办法让我们代码修改完成就直接能看到效果了?
这就是热加载功能。
安装十分简单:
npm install --save-dev electron-reloader
使用方法只需要在main.js的入口文件中引入即可:
// 开启热加载
const reloader = require('electron-reloader')
reloader(module)
五、主进程和渲染进程概念介绍
Electron 运行 package.json
的 main
脚本的进程被称为主进程。 在主进程中运行的脚本通过创建web页面来展示用户界面。 一个 Electron 应用总是有且只有一个主进程。
由于 Electron 使用了 Chromium 来展示 web 页面,所以 Chromium 的多进程架构也被使用到。 每个 Electron 中的 web 页面运行在它的叫渲染进程的进程中。也就是说每个页面都是渲染进程。
在普通的浏览器中,web页面无法访问操作系统的原生资源。 然而 Electron 的用户在 Node.js 的 API 支持下可以在页面中和操作系统进行一些底层交互。
ctrl+shift+i打开渲染进程调试
演示快捷键
六、自定义原生菜单
6.1 自定义菜单
electron默认为我们提供了菜单栏,如上所示。
如果我们不想使用默认的原生菜单,自定义也是可以的。
这需要我们先引入electron的menu菜单对象。
const { app,BrowserWindow,Menu} = require('electron')
然后我们自定义需要先配置模板,再通过模板创建出新的菜单对象,最后将菜单对象放到我们的Menu菜单对象中。
完整的代码为:
// 导入electron对象
// app 控制应用程序的事件生命周期。
// BrowserWindow 创建并控制浏览器窗口。
// Menu 创建原生应用菜单和上下文菜单。
const { app,BrowserWindow,Menu} = require('electron')
// 开启热加载
const reloader = require('electron-reloader')
reloader(module)
// 控制ready事件
// ready事件是:当 Electron 完成初始化时,发出一次。
app.on('ready',()=>{
// 新建窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600
});
// 让主窗口加载文件 html文件
mainWindow.loadFile('./src/index.html');
// 配置模板
const template = [
{
label: '文件'
},
{
label: '编辑'
},
{
label: '关于我们'
}
]
// 菜单对象构建该模板,生成菜单对象
const menu = Menu.buildFromTemplate(template);
// 设置我们构建好的菜单
Menu.setApplicationMenu(menu);
})
ok,我们的菜单栏就彻底改变了。
至于为什么模板中的键值为label,这个是官网要求的具体可查询相关api。Menu | Electron
ok,我们可以参考api丰富一下菜单,这里演示为文件菜单设置一些子菜单。
6.2 给菜单添加点击事件
接下来,我们实现一个功能,就是点击新建窗口的时候,让它弹出一个新的窗口。
实现起来也很容器,template中提供有click()方法来完成。
6.3 抽离菜单定义
为了方便后续维护,我们最好不把所有的功能都写在main.js中。比如菜单栏相关的代码,单独抽取成为一个menu.js。然后再引入到main.js中。
menu.js
const {BrowserWindow,Menu} = require('electron')
// 配置模板
const template = [
{
label: '文件',
submenu: [
{
label: '新建窗口',
click(){
new BrowserWindow({
width: 300,
height: 200
})
}
},
{
label: '保存'
},
{
label: '另存为'
},
{
label: '首选项'
},
{
label: '退出'
}
]
},
{
label: '编辑'
},
{
label: '关于我们'
}
]
// 菜单对象构建该模板,生成菜单对象
const menu = Menu.buildFromTemplate(template);
// 设置我们构建好的菜单
Menu.setApplicationMenu(menu);
main.js
// 导入electron对象
// app 控制应用程序的事件生命周期。
// BrowserWindow 创建并控制浏览器窗口。
// Menu 创建原生应用菜单和上下文菜单。
const { app,BrowserWindow} = require('electron')
// 开启热加载
const reloader = require('electron-reloader')
reloader(module)
// 控制ready事件
// ready事件是:当 Electron 完成初始化时,发出一次。
app.on('ready',()=>{
// 新建窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600
});
// 让主窗口加载文件 html文件
mainWindow.loadFile('./src/index.html');
// 引入自定义菜单
require('./menu')
})
界面效果依然一样,这里就不展示了。
6.4 无边框菜单
我们不用原生的这种菜单方式,像QQ音乐这种,菜单是自定义的。其实实现起来也不复杂。只需要隐藏掉frame即可,然后自己写菜单结构和样式。如下:
我们发现移走了是移走了,可是现在也无法拖拽了,也没有自己的菜单怎么办?
别急,我们一步一步来实现,先自定义菜单。
在index.html中写上菜单的html,然后再为它写一个css样式。
最后是使它能够拖拽,追加css:-webkit-app-region: drag; 实现拖拽。
6.4.1 为无边框菜单添加新建窗口点击事件
上面我们已经实现了无边框的菜单栏,并且让它拥有了可拖拽能力。
下面我们还是要实现一下点击新建窗口弹出一个新的窗口。
这时,问题来了,之前我们都是在主进程中也就是main.js文件中,通过BrowserWindow对象实现的。
现在我们的菜单栏是在index.html文件中实现的,它是在一个渲染进程中。渲染进程是无法操作主进程的对象的。
想在渲染进程中使用主进程中才能使用的对象,这就必须先remote出来。
ok,重新运行一下,发现点击并没有反应,我们打开调试窗口看看,结果发现报错了。
他说require is not defined。
这是因为JavaScript中默认是不能使用require这些属于nodejs的东西的。
如果想在JavaScript代码中使用nodejs的东西,就必须在主进程中开启node模块以及remote模块。
webPreferences: {
// 开启node模块
nodeIntegration: true,// 如果无法开启node模块,还是报这个错,那么请关闭下面这个配置项
contextIsolation: false,
// 开启remote模块
enableRemoteModule: true
}
这里要注意:electron12版本以后,默认是移除了remote的,官方推荐路由的方式来处理。如果想了解的建议直接去官网。这里我们还是选择用remote,既然官网移除了,那么我们再把它安装回来。
npm install @electron/remote --save
问题:
此时再次点击新建窗口,发现还是不能,这是为什么呢?
我们console.log("随便")随便打印点东西看看能否出现结果。结果无法展示出来,说明点击就没起作用。
而我们检查了语法又没有问题。
这是因为我们把整个菜单栏全部都设置成了拖拽功能了,所以才导致无法点击。
解决办法就是将li标签列表设置为不可拖拽。
6.4.2 小总结
总结使用无边框的菜单栏使用步骤:
1. main.js的主窗口中关闭frame。
2. 在对应渲染页面中编写菜单的html结构,并自己设置对应的css样式。
3. 记得在css样式合适的位置设置-webkit-app-region: drag;让其可拖拽,为保障菜单功能点击有效,记得在对应的名称上设置禁止拖拽-webkit-app-region: no-drag;否则父标签的拖拽效果会传递给子标签。
4. 给菜单添加点击事件。
5. 如果要让点击后弹窗,需要使用到Main进程的BrowserWindow。则需要remote出来。
6. 因此需要在主进程开启remote模块。
7. 同时要在js中使用nodejs的东西,则也需要开启node模块。
6.4.2 点击打开网页
同样的道理,我们再来试一试点击打开一个网页。
为了照顾到不同版本带来的remote差异,我们直接用原生的菜单栏。然后把原来自己写的自定义菜单栏html代码注释掉,免得影响美观。
七、打开对话框读取文件
为了照顾到不同版本带来的remote差异,我们直接用原生的菜单栏。然后把原来自己写的自定义菜单栏html代码注释掉,免得影响美观。接着给菜单栏增加一个子菜单:导入文件。
// 导入electron对象
// app 控制应用程序的事件生命周期。
// BrowserWindow 创建并控制浏览器窗口。
// Menu 创建原生应用菜单和上下文菜单。
const { app,BrowserWindow} = require('electron')
// 开启热加载
const reloader = require('electron-reloader')
reloader(module)
// 控制ready事件
// ready事件是:当 Electron 完成初始化时,发出一次。
app.on('ready',()=>{
// 新建窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
// 是否显示原生菜单栏
frame: true,
webPreferences: {
// 开启node模块
nodeIntegration: true,
// 开启remote模块
enableRemoteMoudle: true
}
});
// 让主窗口加载文件 html文件
mainWindow.loadFile('./src/index.html');
// 引入自定义菜单
require('./menu')
// 自动打开调试窗口
mainWindow.webContents.openDevTools();
})
引入dialog并展示,此时就弹出了对话框
下面我们看看可以填写哪些参数:dialog | Electron
由于时间有限,暂时整理到这里,如果有迫切的需要,请联系我,我会以你意想不到的速度进行补充。O(∩_∩)O