官方文档: https://www.electronjs.org/zh/docs/latest/
搭建项目
新建文件夹并初始化项目
mkdir my-electron-app && cd my-electron-app
npm init -y
注意:新生成的package.json的author(作者)和description(描述)字段要填写补全,不然后期打包会打不了
将package.json
入口文件修改为main.js,修改如下:
{"name": "electron","version": "1.0.0","description": "这是我的第一个electron项目","main": "main.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "萧寂","license": "ISC"
}
安装electron
cnpm i --save-dev electron
// 或者
npm i --save-dev electron
// 或者
yarn add --dev electron
安装过程可能会很慢,等着就行了,除非报错
在package.json
下面修改脚本如下,新增一个start命令
{"name": "electron","version": "1.0.0","description": "这是我的第一个electron项目","main": "main.js","scripts": {"start": "electron ."},"keywords": [],"author": "萧寂","license": "ISC","devDependencies": {"electron": "^31.2.0"}
}
根目录下新建main.js
console.log('electron start')
执行yarn run start
控制台如果有打印上面那句话则代表electron项目已经启动了,目前还没创建窗体,至此,项目创建准备已经结束
在主进程创建窗口并显示外部链接页面
main.js
const { app, BrowserWindow } = require('electron')// 当app准备好就执行创建一个窗口
app.on('ready', () => {// 创建窗口let win = new BrowserWindow({x: 100,y: 50, //窗体坐标show: true, // false为不展示窗体,默认true展示,相当于将窗口隐藏了width: 800,height: 600, //长宽// maxHeight: 600,// maxWidth: 1000, //最大宽高minHeight: 200,minWidth: 300, //最小宽高resizable: true, //是否允许缩放title: "萧寂", //标题(加上这个属性,在页面中就不要有title标签了)// icon: "./icon.png", //设置icon图标// frame: false, //只保留主体部分,不保留其他的选项卡窗口了,隐藏菜单栏// transparent: true, //将窗体完全透明化autoHideMenuBar: true, //只保留标题,不保留其他的选项卡部分,也是隐藏菜单栏意思alwaysOnTop:true, // 将窗口置顶})win.loadURL('https://xiaojiblog.netlify.app/') // 打开外部链接
})
运行yarn run start
效果图
支持放大全屏,也可以在上面参数里面把最大宽度高度限制放开,不设置的话就默认可以全屏
更多配置项参考:https://www.electronjs.org/zh/docs/latest/api/base-window#class-basewindow
在主进程加载并显示本地页面
根目录下创建/pages/index.html和/pages/index.css
/pages/index.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><link rel="stylesheet" href="./index.css">
</head><body><h1>第一个本地electron页面</h1>
</body></html>
/pages/index.css
内容如下
h1{background-color: gray;color: orange;
}
修改主进程代码main.js
如下
const { app, BrowserWindow } = require('electron')// 当app准备好就执行创建一个窗口
app.on('ready', () => {// 创建窗口(下面的配置在上面讲过了,这里就删掉了,只留下三个)let win = new BrowserWindow({width: 800,height: 600, //长宽autoHideMenuBar: true, //只保留标题,不保留其他的选项卡部分,也是隐藏菜单栏意思})win.on('close', () => {// 从性能考虑,应该释放窗体这个变量,删除窗体引用win = null})win.loadFile('./pages/index.html') // loadFile就是加载本地页面的,loadURL加载的是在线链接
})
运行yarn run start
效果图如下:
electron生命周期事件
ready:app初始化完成 //重要
dom-ready:一个窗口中的文本加载完成 //重要
did-finsh-load:导航完成时触发 //重要
window-all-closed:所有窗口都被关闭时触发 //重要
before-quit:在关闭窗口之前触发
will-quit:在窗口关闭并且应用退出时触发
quit:当所有窗口被关闭时触发
close:当窗口关闭时触发,此时应删除窗口引用
main.js代码
const { app, BrowserWindow } = require("electron")
const createWindow = () => {// 创建窗口let win = new BrowserWindow({width: 800,height: 600,})//当前窗口显示的页面win.loadFile("index.html")// 这个webContents对象可以控制dom元素加载事件win.webContents.on('did-finish-load', () => {console.log('3333->did-finish-load')})win.webContents.on('dom-ready', () => {console.log('2222->dom-ready')})// 窗口关闭win.on('close', () => {console.log('8888->close')// 从性能考虑,应该释放窗体这个变量,删除窗体引用win = null})
}// 生命周期
// 通过on监听事件
app.on('ready', () => {console.log("1111->ready")createWindow()
})app.on("window-all-closed", () => {// 如果监听了window-all-closed这个事件,需要在事件里面主动退出应用,没有监听事件的话默认会直接退出应用// 但如果监听了此事件,但没有退出操作的话,后续的567生命周期也不会执行console.log("4444->window-all-closed")//退出应用app.quit()
})app.on("before-quit", () => {console.log("5555->before-quit")
})app.on("will-quit", () => {console.log("6666->will-quit")
})app.on("quit", () => {console.log("7777->quit")
})
从打开窗体到关闭窗体打印结果如下
打开控制台调试工具
在当前窗口按下ctrl+shift+i
效果图:
刷新页面
按下ctrl+r
启动项目遇到的两个警告
1.自动填充问题,在vscode终端打印的警告内容,官方未做处理,electron开发者回复不影响开发可以先忽略,警告内容如下:
[892:0714/014647.854:ERROR:CONSOLE(1)] "Request Autofill.enable failed. {"code":-32601,"message":"'Autofill.enable' wasn't found"}", source: devtools://devtools/bundled/core/protocol_client/protocol_client.js (1)
[892:0714/014647.854:ERROR:CONSOLE(1)] "Request Autofill.setAddresses failed. {"code":-32601,"message":"'Autofill.setAddresses' wasn't found"}", source: devtools://devtools/bundled/core/protocol_client/protocol_client.js (1)
2.在窗口的ctrl+shift+i
的控制台,会有个warning的报错,是关于(Insecure Content-Security-Policy)
的,是内容安全的警告,解决方式如下:
在html页面上新增一个meta标签,内容如下
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;">
然后再次启动项目就会发现警告没了
完善窗口关闭行为
在Windows和Linux上,关闭所有窗口通常会完全退出一个应用程序。
但是在mac上,关闭所有窗口并不会完全退出程序,因此这里需要对mac电脑进行一个判断
官方介绍: https://www.electronjs.org/zh/docs/latest/tutorial/quick-start#%E7%AE%A1%E7%90%86%E7%AA%97%E5%8F%A3%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F
在主进程加上下面的事件和判断,修改main.js
如下:
const { app, BrowserWindow } = require('electron')// 创建窗口
function createWindow () {// 创建窗口let win = new BrowserWindow({width: 800,height: 600, //长宽autoHideMenuBar: true, //只保留标题,不保留其他的选项卡部分,也是隐藏菜单栏意思})win.on('close', () => {// 从性能考虑,应该释放窗体这个变量,删除窗体引用win = null})win.on('close', () => {// 从性能考虑,应该释放窗体这个变量,删除窗体引用win = null})win.loadFile('./pages/index.html')
}// 当app准备好就执行创建一个窗口
app.on('ready', () => {createWindow()// 监听应用被激活app.on('activate', () => {// 当应用激活后,窗口数量为0时,重新创建一个窗口(mac使用,在windows和Linux窗口为0直接退出了)if (BrowserWindow.getAllWindows().length === 0) createWindow()})
})// 监听所有窗口都被关闭事件
app.on('window-all-closed', () => {// 不是mac系统就执行退出if (process.platform !== 'darwin') app.quit()
})
配置热更新自动重启
安装插件
yarn add nodemon -D
修改package.json
下面的script节点的start命令如下:
"start": "nodemon --exec electron ."
运行yarn run start
然后会发现.html后缀的文件内容更换了并不会自动重启项目,只有main.js主进程代码更换了才重启,为了解决这个问题,继续往下看
根目录下新建nodemon.json
文件,内容如下:
{"ignore": [ // 忽略那些文件夹"node_modules","dist"],"restartable": "r", // 短命令,当文件没更改,在终端输入字母r可以自动重启"watch": ["*.*"], // 监视所有文件"ext": "js,json,html,css,vue" // 包含的文件名后缀
}
运行yarn run start
再次重启项目就会发现一切正常,都可以实现自动刷新了
主进程与渲染进程进行通信(下面两种方式选其一即可)
简单来说就是渲染进程浏览器环境不能调用主进程的nodejs的相关api和方法,进行通信后就可以了,下面是两种方式,
第一种是:将渲染进程设置为可以访问nodejs方法,这时候渲染进程拥有了访问window环境和nodejs环境的方法和api调用
第二种是:使用预加载脚本,当有需求要操作nodejs的api调用时,渲染进程调用预加载脚本的对应方法,向主进程发起通信事件,主进程接收到对应的事件后就会执行操作
一:在渲染进程直接使用nodejs的所有api调用,本人常用方式(简单)
后面所有的案例都是使用这个方式在渲染进程里面做操作
需求: 用户输入文字后获取输入框内容并写入本地hello.txt文件内,点击第二个按钮可以将写入的文件内容信息弹出来
修改/pages/index.html内容如下:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="Content-Security-Policy"content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"><title>Document</title><link rel="stylesheet" href="./index.css">
</head><body><h1>第haha本地electron页面</h1><input type="text" id="input"><br><button id="btn1">向D盘写入文件hello.txt</button><br><button id="btn2">读取D盘hello.txt文件</button><script src="./render.js"></script>
</body></html>
修改main.js如下
const { app, BrowserWindow } = require('electron')
const path = require('path')
// 创建窗口
function createWindow () {// 创建窗口let win = new BrowserWindow({width: 800,height: 600, //长宽autoHideMenuBar: true, //只保留标题,不保留其他的选项卡部分,也是隐藏菜单栏意思webPreferences: {nodeIntegration: true, // 配置这三个选项就可以实现在渲染进程使用nodejs的api调用contextIsolation: false,enableRemoteModule: true}})win.on('close', () => {win = null})win.loadFile('./pages/index.html')
}// 当app准备好就执行创建一个窗口
app.on('ready', () => {createWindow()
})
渲染进程render.js
内容改为如下
const fs = require('fs')
const btn = document.getElementById('btn1')
const btn2 = document.getElementById('btn2')
const input = document.getElementById('input')
btn.addEventListener('click', () => {// 向D盘写入文件fs.writeFileSync('D:/hello.txt', input.value) // 直接调用nodejs写入文件的方法
})
btn2.addEventListener('click', async ()