1.创建工程
然后疯狂敲键盘回车
修改配置, 注意author和description是必填项
还有就是需要再scripts中添加启动脚本
1 2 3 4 5 6 7 8 9 10 11
| { "name": "electron-test", "version": "1.0.0", "description": "this is a simple electron demo", "main": "main.js", "scripts": { "start": "electron ." }, "author": "肉豆蔻吖", "license": "ISC" }
|
1 2
| npm install --save-dev electron
|
根目录下创建main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| console.log('hello electron');
const { app, BrowserWindow } = require('electron');
app.on('ready', () => { const win = new BrowserWindow({ width: 800, height: 600, autoHideMenuBar: true, }) win.loadURL('http://xiamu.icu/') })
|
效果如下:
2.本地静态资源
创建pages/index.html
pages/index.css
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13
| <!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> <link rel="stylesheet" href="./index.css"> </head> <body> <h1>欢迎学习Electron开发</h1> </body> </html>
|
index.css
1 2 3 4
| h1 { background-color: gray; color: orange; }
|
修改main.js, 使用win.loadFile(‘./pages/index.html’)函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
const { app, BrowserWindow } = require('electron');
app.on('ready', () => { const win = new BrowserWindow({ width: 800, height: 600, autoHideMenuBar: true, }) win.loadFile('./pages/index.html') })
|
然后npm start
, 启动
按下快捷键Ctrl Shift I可以打开熟悉的浏览器控制台
打开控制台之后, 有个一个警告, 这个警告可以直接忽略, Request Autofill.enable failed
但是浏览器的控制台有一个警告, 这个警告不能忽略
在网页中添加
1 2 3
| <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';" />
|
按下Ctrl r进行刷新, Electron应用控制台就没有警告了
参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP
https://www.electronjs.org/zh/docs/latest/tutorial/quick-start
完善窗口行为
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
const { app, BrowserWindow } = require('electron');
function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, autoHideMenuBar: true, }) win.loadFile('./pages/index.html') }
app.on('ready', () => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) })
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() })
|
配置自动重启(热部署)
安装Nodemon
在package.json
启动脚本命令修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "name": "electron-test", "version": "1.0.0", "description": "this is a simple electron demo", "main": "main.js", "scripts": { "start": "nodemon --exec electron ." }, "author": "肉豆蔻吖", "license": "ISC", "devDependencies": { "electron": "^31.0.2" } }
|
根目录下创建nodemon.json
1 2 3 4 5 6 7 8 9
| { "ignore": [ "node_modules", "dist" ], "restartable": "r", "watch": ["*.*"], "ext": "html,js,css" }
|
重启 npm start
每当保存的时候,页面就会自动刷新了,或者在vscode的控制台上手动扣一下r
主进程和渲染进程
主进程就是main.js
渲染进程是render.js
main.js相当于是vscode的控制台,render.js相当于是浏览器的控制台
pages/render.js
1 2 3 4 5
| const btn1 = document.getElementById('btn1')
btn1.onclick = () => { alert('你好啊!') }
|
然后修改index.html代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!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> <link rel="stylesheet" href="./index.css"> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';" /> </head> <body> <h1>欢迎学习Electron开发!!</h1> <button id="btn1">点我</button> <script type="text/javascript" src="./render.js"></script> </body> </html>
|
主进程和渲染进程,这两者想要进行通信,就得借助一个预加载脚本
preload脚本
根目录创建preload.js
main.js与preload建立联系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
const { app, BrowserWindow } = require('electron'); const path = require('path')
function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, autoHideMenuBar: true, webPreferences: { preload: path.resolve(__dirname, './preload.js') } }) win.loadFile('./pages/index.html') }
app.on('ready', () => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) })
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() })
|
preload.js
1 2 3 4 5 6 7
| console.log('preload');
const { contextBridge } = require('electron');
contextBridge.exposeInMainWorld('myAPI', { version: process.version })
|
render.js
1 2 3 4 5 6
| const btn1 = document.getElementById('btn1')
btn1.onclick = () => { console.log(window); alert(myAPI.version) }
|
最终页面效果,渲染进程成功获取到了主进程的版本号了
预加载脚本只能使用部分的主进程的函数,比如process
, 不能够使用 __dirname
进程通信(IPC)
1.渲染进程 => 主进程(单向)
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <!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> <link rel="stylesheet" href="./index.css"> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';" /> </head> <body> <h1>欢迎学习Electron开发!!</h1> <button id="btn1">点我</button> <br> <br> <hr> <input id="input" type="text"> <button id="btn2">向D盘写点东西</button> <script type="text/javascript" src="./render.js"></script> </body> </html>
|
render.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| console.log('render');
const btn1 = document.getElementById('btn1') const btn2 = document.getElementById('btn2') const input = document.getElementById('input')
btn1.onclick = () => { console.log(window); alert(myAPI.version) }
btn2.onclick = () => { myAPI.saveFile(input.value) }
|
preload.js
1 2 3 4 5 6 7 8 9 10
| console.log('preload');
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('myAPI', { version: process.version, saveFile: (data) => { ipcRenderer.send('file-save', data) } })
|
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
const { app, BrowserWindow, ipcMain } = require('electron'); const path = require('path') const fs = require('fs');
function writeFile(_, data) { fs.writeFileSync('D:/hello.txt', data) }
function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, autoHideMenuBar: true, webPreferences: { preload: path.resolve(__dirname, './preload.js') } }) ipcMain.on('file-save', writeFile) win.loadFile('./pages/index.html') }
app.on('ready', () => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) })
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() })
|
2.渲染进程 <=> 主进程(双向)
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <!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> <link rel="stylesheet" href="./index.css"> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';" /> </head> <body> <h1>欢迎学习Electron开发!!</h1> <button id="btn1">点我</button> <br> <br> <hr> <input id="input" type="text"> <button id="btn2">向D盘写点东西hello.txt</button> <br> <br> <hr> <button id="btn3">读取D盘中的hello.txt</button> <script type="text/javascript" src="./render.js"></script> </body> </html>
|
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
const { app, BrowserWindow, ipcMain } = require('electron'); const path = require('path') const fs = require('fs');
function writeFile(_, data) { fs.writeFileSync('D:/hello.txt', data) }
function readFile() { return fs.readFileSync('D:/hello.txt').toString() }
function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, autoHideMenuBar: true, webPreferences: { preload: path.resolve(__dirname, './preload.js') } }) ipcMain.on('file-save', writeFile) ipcMain.handle('file-read', readFile) win.loadFile('./pages/index.html') }
app.on('ready', () => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) })
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() })
|
preload.js
1 2 3 4 5 6 7 8 9 10 11 12 13
| console.log('preload');
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('myAPI', { version: process.version, saveFile: (data) => { ipcRenderer.send('file-save', data) }, readFile() { return ipcRenderer.invoke('file-read') } })
|
render.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| console.log('render');
const btn1 = document.getElementById('btn1') const btn2 = document.getElementById('btn2') const btn3 = document.getElementById('btn3') const input = document.getElementById('input')
btn1.onclick = () => { console.log(window); alert(myAPI.version) }
btn2.onclick = () => { myAPI.saveFile(input.value) }
btn3.onclick = async () => { let data = await myAPI.readFile() alert(data) }
|
运行效果:
打包
安装打包工具
1
| npm install electron-builder -D
|
添加配置
package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| { "name": "electron-test", "version": "1.0.0", "description": "this is a simple electron demo", "main": "main.js", "scripts": { "start": "nodemon --exec electron .", "build": "electron-builder" }, "build": { "appId": "icu.xiamu.video", "win": { "icon": "./logo.ico", "target": [ { "target": "nsis", "arch": [ "x64" ] } ] }, "nsis": { "oneClick": false, "perMachine": true, "allowToChangeInstallationDirectory": true } }, "author": "肉豆蔻吖", "license": "ISC", "devDependencies": { "electron": "^31.0.2", "electron-builder": "^24.13.3", "nodemon": "^3.1.4" } }
|
根目录准备一个logo.ico
然后进行打包
electron-vite
https://electron-vite.org/