1、electron
- 一款应用广泛的跨平台的桌面应用开发框架
- electron本质是结合了chromium与node.js
- 使用html、css、js等web技术构建桌面应用程序(vue tsx less ts)
2、 electron流程模型
main 可以__dirname fs
render进程可以 alert() window()
3、创建
1、新建一个文件夹
2、执行
npm init
3、对package.json修改
必须设置author和description 否则不能打包
"main": "main.js"
{
"name": "electron-test",
"version": "1.0.0",
"description": "Hello World",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "liweina",
"license": "ISC",
"devDependencies": {
"electron": "^33.2.0"
}
}
4、安装electron依赖
npm i electron -D
5、创建一个文件main.js
main.js 创建一个窗口
js
// app相当于桌面应用 ,地基 BrowserWindow浏览器窗口 所有窗口都需要它new出来
const { app, BrowserWindow } = require('electron')
// 当应用准备好时会调用
app.on('ready', () => {
// 创建一个窗口
let win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true, //自动隐藏菜单栏
x: 0,
y: 0, //窗口的位置 在左上角
alwaysOnTop: true //窗口一直置顶
})
win.loadURL('https://www.baidu.com')
})
6、运行 npm start
4、加载一个本地页面
- main.js修改
js
//将url加载换成这个就行
win.loadFile('./pages/index.html') //加载一个文件
- 在index.html编写代码
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>index</title>
</head>
<body>
<h1>
你好啊!
</h1>
</body>
- 此时开发者⼯具会报出⼀个安全警告,需要修改index.html ,配置 CSP(Content-Security-Policy)
html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; st
yle-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中,⽽不是通过外部链接引⽤。
关于 CSP 的详细说明请参考:
Content-Security-Policy
electron-security
5、打开控制台 ctrl+shift+i
在窗口打开控制台 ctrl+shift+i
6、完善窗口行为
windows和Linux:应用所有的窗口都关闭,那么应用自动退出
js
// 当所有的窗口都关闭时
app.on('window-all-closed', () => {
// 不是苹果系统 退出
if (process.platform !== 'darwin') app.quit()
})
macos: 应用退出了,但其实在Dock栏里还能看出
mac应用即使在没有打开窗口的情况下也继续运行,并且在没有窗口可用的情况下激活应用时会打开新的窗口
js
// 当应用准备好时会调用
app.on('ready', () => {
createWindow()
// 当窗口被激活的时候, 这是给苹果电脑看的
app.on('activate', () => {
// 当前窗口数量是0,就创建一个窗口
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
7、配置自动重启
1、安装nodemon
js
npm i nodemon -D
更改package.json
js
"start": "nodemon --exec electron ."
配置nodemon.json规则
js
{
"ignore": ["node_modules", "dist"],
"restartable": "r",
"watch": ["*.*"],
"ext": "html,js,css"
}
main.js是运行在主进程
如果main.js打印控制台中文乱码,更改package.json
js
"start": "chcp 65001 && nodemon --exec electron ."
8、主进程与渲染进程
主进程 main.js
js
// app相当于桌面应用 ,地基 BrowserWindow浏览器窗口 所有窗口都需要它new出来
const { app, BrowserWindow } = require('electron')
const path = require('path')
function createWindow() {
// 创建一个窗口
let win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true, //自动隐藏菜单栏
webPreferences: {
preload: path.resolve(__dirname, './preload.js') //写绝对路径)
},
x: 0,
y: 0, //窗口的位置 在左上角
alwaysOnTop: true //窗口一直置顶
})
// win.loadURL('https://www.baidu.com') //加载一个url
win.loadFile('./pages/index.html') //加载一个文件
}
console.log(process.version)
console.log(process.versions.chrome)
// 当应用准备好时会调用
app.on('ready', () => {
console.log('应用准备好了easy1111')
createWindow()
// 当窗口被激活的时候, 这是给苹果电脑看的
app.on('activate', () => {
// 当前窗口数量是0,就创建一个窗口
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// 当所有的窗口都关闭时
app.on('window-all-closed', () => {
// 不是苹果系统 退出
if (process.platform !== 'darwin') app.quit()
})
渲染进程 和html并列的render.js
js
const btn1 = document.querySelector('#btn1')
btn1.onclick = () => {
alert('点我了')
console.log(versions.version)
}
console.log('render')
console.log(window)
console.log(versions.version)
9、preload脚本
预加载脚本 运行在渲染进程上 能执行一部分的node api 通过它联系主进程和渲染进程 主进程和渲染进程的桥梁
js
// 预加载脚本 运行在渲染进程上 能执行一部分的node api 通过它联系主进程和渲染进程
console.log('preload', process.version)
// 在主进程和渲染进程之间安全地创建一个受限的通信桥梁
const { contextBridge } = require('electron')
// 主世界曝光 暴露给渲染进程
contextBridge.exposeInMainWorld('versions', {
version: process.version,
node: process.versions.node
})
10、进程通信
值得注意的是:
上文中的preload.js,无法使用全部Node的API,比如:不能使用Node中的fs模块,但主进程(main.js)是可以的
这时就需要进程通信了 简单就是说,要让preload.js通知main.js去调用fs模块去干活
关于Electron进程通信,我们要知道:
- IPC全称是InterProcess Communication 即进程通信
- IPC是Electron中最为核心的内容,它是从UI调用原生API的唯一方法
- Electron中,主要使用ipcMain 和ipcRenderer来定义通道,进行进程通信
10.1渲染进程->主进程 单向
概述:在渲染进程中ipcRenderer.send 发送消息,在主进程使用ipcMain.on接受消息
常用于:在web中调用主进程的API,例如下面的这个需求:
需求:点击按钮后,在用户的D盘创建一个hello.txt文件,文件内容;来源与用户输入
index.html
html
<input id="input" type="text" />
<button id="btn2">向D盘写入hello.txt</button>
render.js
js
const input = document.querySelector('#input')
const btn2 = document.querySelector('#btn2')
btn2.onclick = () => {
myAPI.saveFile(input.value)
}
preload.js
js
const input = document.querySelector('#input')
const btn2 = document.querySelector('#btn2')
btn2.onclick = () => {
myAPI.saveFile(input.value)
}
main.js
js
const { app, BrowserWindow, ipcMain } = require('electron')
// 调用node的fs模块 和文件系统交互,操作文件和目录
const fs = require('fs')
//写入文件
function writeFile(_, data) {
// 同步写入文件 会堵塞进程
fs.writeFileSync('D:/hello.txt', data)
}
function createWindow() {
/********/
//主进程注册对应回调
ipcMain.on('file-save', writeFile)
win.loadFile('./pages/index.html') //加载一个文件
}
10.2 渲染进程<--->主进程 双向
概述:渲染进程通过ipcRenderer.invokie 发送消息,主进程使用ipcMain.handle 接收并处理消息
适合需要主进程处理并返回结果的交互场景
备注:ipcRender.invoke的返回值是Promise实例。
常用于:从渲染器进程调用主线程方法并等待结果
需求:点击按钮从D盘读取hello.txt中的内容,并将结果呈现到页面上
index.html
html
<button id="btn3">读取D盘的hello.txt</button>
render.js
js
const btn3 = document.querySelector('#btn3')
btn3.onclick = async () => {
let data = await myAPI.readFile()
alert(data)
}
preload.js
js
contextBridge.exposeInMainWorld('myAPI', {
version: process.version,
node: process.versions.node,
saveFile: data => {
ipcRenderer.send('file-save', data)
},
readFile() {
return ipcRenderer.invoke('file-read')
}
// async readFile() {
// // invoke有调用的意思 invoke返回值是promise
// let x = await ipcRenderer.invoke('file-read')
// console.log(x)
// }
})
main.js
js
const { app, BrowserWindow, ipcMain } = require('electron')
// 调用node的fs模块 和文件系统交互,操作文件和目录
const fs = require('fs')
function readFile() {
// 读出来东西是buffer
return fs.readFileSync('D:/hello.txt').toString()
}
function createWindow() {
/*******/
ipcMain.handle('file-read', readFile)
win.loadFile('./pages/index.html') //加载一个文件
}
10.3 主进程->渲染进程
render.js
js
//当页面的所有内容都已经完全加载并初始化完成后就会执行此函数
window.onload = () => {
myAPI.mainSend(getMainSendCallback)
}
function getMainSendCallback(event,data) {
console.log(11111)
console.log(event, data)
}
preload.js
js
contextBridge.exposeInMainWorld('myAPI', {
version: process.version,
node: process.versions.node,
mainSend: callback => {
return ipcRenderer.on('main-send', callback)
}
})
main.js
js
const { app, BrowserWindow, ipcMain } = require('electron')
function createWindow() {
/**********/
win.loadFile('./pages/index.html') //加载一个文件
//必须加载完后才能发送消息
setTimeout(() => {
win.webContents.send('main-send', 11111)
}, 600)
}
11.打包应用
使用electron-builder打包应用
1、安装electron-builder:
js
npm install electron-builder -D
2、在package.json中进行相关配置,具体配置如下:
json
{
"name": "video-tools", // 应⽤程序的名称
"version": "1.0.0", // 应⽤程序的版本
"main": "main.js", // 应⽤程序的⼊⼝⽂件
"scripts": {
"start": "electron .", // 使⽤`electron .` 命令启动应⽤程序
"build": "electron-builder" // 使⽤`electron-builder` 打包应⽤程序,⽣成安装包
},
"build": {
"appId": "com.atguigu.video", // 应⽤程序的唯⼀标识符
// 打包windows平台安装包的具体配置
"win": {
"icon":"./logo.ico", //应⽤图标
"target": [
{
"target": "nsis", // 指定使⽤NSIS 作为安装程序格式
"arch": ["x64"] // ⽣成64 位安装包
}
]
},
"nsis": {
"oneClick": false, // 设置为`false` 使安装程序显示安装向导界⾯,⽽不是⼀键安装
"perMachine": true, // 允许每台机器安装⼀次,⽽不是每个⽤户都安装
"allowToChangeInstallationDirectory": true // 允许⽤户在安装过程中选择安装⽬录
}
},
"devDependencies": {
"electron": "^30.0.0", // 开发依赖中的 Electron 版本
"electron-builder": "^24.13.3" // 开发依赖中`electron-builder` 版本
},
"author": "tianyu", // 作者信息
"license": "ISC", // 许可证信息
"description": "A video processing program based on Electron" // 应⽤程序的描述
}
12.electron-vite
实际开发可以用这个框架 更便捷和好使用