Electron 安装以及搭建一个工程
安装Node.js
在使用Electron进行开发之前,需要安装 Node.js。 官方建议使用最新的LTS版本。
检查 Node.js 是否正确安装:
# 查看node版本
node -v
# 查看npm版本
npm -v
注意
开发者需要在开发环境安装 Node.js 才能编写 Electron 项目,但是 Electron 应用程序 不使用系统本地的 Node.js 环境来运行它的代码。
Electron 应用程序 使用它内置的 Node.js 运行时。 这意味着 终端用户 不需要安装 Node.js 环境也可以运行Electron 应用程序。
安装Electron
Electron 应用程序遵循与其他 Node.js 项目相同的结构。
首先创建一个文件夹my-electron-app
并在vscode 编译器中打开文件夹。
初始化 npm 包:
npm init
init 初始化命令会提示开发者在项目初始化配置中设置一些值,有几条规则需要遵循:
entry point
应为main.js
。author
与description
可为任意值,但对于应用打包是必填项。
生成的 package.json
文件应该像这样:
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "一个测试用的electron项目",
"main": "main.js",
"author": "zhangSan",
"license": "ISC"
}
在项目中安装electron
:
npm install --save-dev electron
# 可以简写
npm i electron -D
注意 :electron是开发依赖。
electron依赖非常难下载,总是下载到中途就失败了。
查看官方文档:
在运行 npm install electron 时,有些用户会偶尔遇到安装问题。
在大多数情况下,这些错误都是由网络问题导致,而不是因为 electron npm 包的问题。 如ELIFECYCLE
、EAI_AGAIN
、ECONNRESET
和ETIMEDOUT
等错误都是此类网络问题的标志。 最佳的解决方法是尝试切换网络,或是稍后再尝试安装。
fine,意思是网络问题咯,千M网下载不下来也是离谱啊…
解决方案
-
如果通过
npm
安装失败,你也可以尝试通过从 electron/electron/release 直接下载 Electron -
如果安装失败并报错
EACCESS
,可能需要修复npm权限。 -
如果上述报错持续出现,unsafe-perm 标志可能需要被设置为
true
:
npm install electron --unsafe-perm=true
- 在较慢的网络上, 最好使用
--verbose
标志来显示下载进度:
npm install --verbose electron
- 如果需要强制重新下载文件, 并且
SHASUM
文件将force_no_cache
环境变量设置为true
。
我直接使用了第3点和第4点:
npm install electron -D --unsafe-perm=true --verbose
历时半个小时,这个依赖终于下载成功了! 感天动地!!
在 package.json
配置文件中的scripts
字段下增加一条start
命令:
{
"scripts": {
"start": "electron ."
}
}
package.json
文件应该像这样:
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "一个测试用的electron项目",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "zhangSan",
"license": "ISC",
"devDependencies": {
"electron": "^32.1.2"
}
}
执行start
命令能在开发模式下打开应用:
npm start
执行命令直接就报错了:
没有找到模块main.js,请确认 package.json
中的 main
入口是合法的。
运行主进程
任何 Electron 应用程序的入口都是 main
文件。 这个文件控制了主进程,它运行在一个完整的Node.js环境中,负责控制应用的生命周期,显示原生界面,执行特殊操作并管理渲染器进程。
执行 npm start
期间,Electron 将依据应用中 package.json
配置下main
字段中配置的值查找此文件。
在项目的根目录下创建一个名为 main.js
的空文件。
再次执行 npm start
,不再报错,应用成功启动。但是,它不会做任何事,开发者也看不到启动的应用窗口,因为 main.js
是空白文件,没有添加任何代码!
在 main.js
中编写代码,创建⼀个基本窗⼝:
// main.js运⾏在应⽤的主进程上,⽆法访问Web相关API
// main.js主要负责:控制⽣命周期、显示界⾯、控制渲染进程等其他操作。
// 从electron中引入app、BrowserWindow
// app模块: 控制应用程序的事件生命周期。
// BrowserWindow模块: 创建和管理应用程序 窗口
const { app, BrowserWindow } = require('electron')
function createWindow() {
const win = new BrowserWindow({
width: 500, // 窗口宽度
height: 300 // 窗口高度
})
}
// 在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口。
app.on('ready', () => {
// 当app准备好以后,创建窗口
createWindow()
})
注意
可以通过使用 app.whenReady()
API来监听 app
模块的 ready
事件:
app.whenReady().then(() => {
createWindow()
})
再次执行 npm start
,应用启动成功,并且能看到应用窗口:
下图是应用自带的菜单:
如果不要菜单,需要在创建窗口时配置:autoHideMenuBar: true
修改 main.js
,在窗口中展示一个远程页面:
const { app, BrowserWindow } = require('electron')
function createWindow() {
const win = new BrowserWindow({
width: 500,
height: 300,
autoHideMenuBar: true
})
// 展示一个远程页面
win.loadURL('https://lol.qq.com/main.shtml')
// 可以在这里添加窗口的其他配置和事件处理
}
app.on('ready', () => {
// 当app准备好的时候,创建窗口
createWindow()
})
执行npm start
,查看效果:
关于BrowserWindow的更多配置项,请参考官网:BrowserWindow实例属性
加载本地页面
在Electron中,各个窗口显示的内容可以是本地HTML文件,也可以是一个远程url。
创建 pages/index.html
编写内容::
<!DOCTYPE html>
<html lang="en">
<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'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
/>
<title>Hello Electron!</title>
</head>
<body>
<h1>Hello Electron!</h1>
</body>
</html>
修改 mian.js
加载本地⻚⾯:
// 加载⼀个本地⻚⾯
win.loadFile('./pages/index.html')
执行npm start
,查看效果:
可以通过快捷键ctrl+shift+i,打开控制台:
CSP配置说明
语法:
Content-Security-Policy: <policy-directive>; <policy-directive>
这是一个用于设置网页内容安全策略(Content Security Policy,简称 CSP)的 HTML 标签:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
/>
上述配置的说明:
default-src 'self'
default-src
:配置加载策略,适⽤于所有未在其它指令中明确指定的资源类型。self
:仅允许从同源的资源加载,禁⽌从不受信任的外部来源加载,提⾼安全性。
style-src 'self' 'unsafe-inline'
style-src
:指定样式表(CSS)的加载策略。self
:仅允许从同源的资源加载,禁⽌从不受信任的外部来源加载,提⾼安全性。unsafe-inline
:允许在HTML⽂档内使⽤内联样式。
img-src 'self' data:
img-src
:指定图像资源的加载策略。self
:表示仅允许从同源加载图像。data:
:允许使⽤data: URI
来嵌⼊图像。这种URI模式允许将图像数据直接嵌
⼊到HTML或CSS中,⽽不是通过外部链接引⽤。
HTTP 响应头 Content-Security-Policy
允许站点管理者控制用户代理能够为指定的页面加载哪些资源。除了少数例外情况,设置的政策主要涉及指定服务器的源和脚本结束点。
关于 CSP 的详细说明请参考:MDN-Content-Security-Policy、Electron Security
如果没有配置CSP,控制台会有⼀个安全警告:
管理窗口的生命周期
创建窗口
- 通常在主进程中创建窗口,可以使用
BrowserWindow
类来创建窗口实例。
const { app, BrowserWindow } = require('electron')
function createWindow() {
const win = new BrowserWindow({
width: 500,
height: 300,
autoHideMenuBar: true
})
// 展示一个远程页面
win.loadURL('https://lol.qq.com/main.shtml')
// 可以在这里添加窗口的其他配置和事件处理
}
app.on('ready', () => {
// 当app准备好的时候,创建窗口
createWindow()
})
窗口的显示与隐藏
win.show()
:显示窗口。通常在应用启动后或在特定事件触发时调用。win.hide()
:隐藏窗口。可以在需要隐藏窗口的情况下调用,比如最小化到系统托盘时。
窗口最小化、最大化、恢复
win.minimize()
:将窗口最小化到任务栏或 Dock。win.maximize()
:最大化窗口。win.unmaximize()
:如果窗口处于最大化状态,可以使用win.unmaximize()
恢复到原始大小。
窗口事件监听
-
closed
事件:- 当窗口关闭时触发。在这个事件处理函数中,可以进行一些清理工作,比如释放资源。
win.on('closed', () => { mainWindow = null; });
-
resize
事件:
当窗口大小改变时触发。可以在这个事件处理函数中根据新的窗口大小调整应用的布局或进行其他相应的处理。 -
move
事件:
当窗口位置改变时触发。可以用于记录窗口位置或进行与窗口位置相关的操作。
关闭所有窗口时退出应用 (Windows & Linux)
在Windows 和 Linux 系统:关闭所有窗口会完全退出应用程序。
为了实现这一点,需要监听 app
模块的 'window-all-closed'
事件。如果用户不是在 macOS(darwin
) 上运行程序,则调用 app.quit()
。
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
如果没有窗口打开则打开一个窗口 (macOS)
在 Linux 和 Windows 上,应用根本不存在是否被激活的说法,应用要么是有进程,要么是没进程。
但是,在 macOS 上,应用已经不运行了,但是应用处于未激活状态:应用在没有打开任何窗口的情况下也继续运行。
如果激活应用,则检查应用是否 有窗口,没有窗口就在激活应用时会打开新的窗口。
监听 app
模块的 activate
事件来实现:如果没有任何浏览器窗口是打开的,则调用 createWindow()
方法。
// 当app准备好后,执⾏createWindow创建窗⼝
app.on('ready', () => {
createWindow();
// 当应⽤被激活时
app.on('activate', () => {
// 如果当前应⽤没有窗⼝,则创建⼀个新的窗⼝
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
完整的 main.js
如下:
const { app, BrowserWindow } = require('electron');
function createWindow() {
const win = new BrowserWindow({
width: 500, // 窗口宽度
height: 300, // 窗口高度
autoHideMenuBar: true // 隐藏菜单栏
});
// 在窗口中加载一个远程页面
win.loadFile('./pages/index.html');
}
app.on('ready', () => {
// 当app准备好的时候,创建窗口
createWindow();
// 在Windows & Linux,应用的所有窗口关闭时,退出应用程序
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
// 在macOS,当应⽤被激活时
app.on('activate', () => {
// 如果当前应⽤没有窗⼝,则创建⼀个新的窗⼝
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});