前言
本文將介紹electron基本使用和構(gòu)建electron+vite+vue3腳手架開發(fā)項(xiàng)目,帶你快速入門。
一、electron是什么?
Electron 是一個(gè)使用 JavaScript、HTML 和 CSS 構(gòu)建桌面應(yīng)用的框架。 通過將 Chromium 和 Node.js 嵌入到其二進(jìn)制文件中,Electron 允許你維護(hù)一個(gè) JavaScript 代碼庫并創(chuàng)建可在 Windows、macOS 和 Linux 上運(yùn)行的跨平臺(tái)應(yīng)用 — 無需原生開發(fā)經(jīng)驗(yàn)。
二、electron 進(jìn)程模型
1.主進(jìn)程
每個(gè) Electron 應(yīng)用都有一個(gè)主進(jìn)程,充當(dāng)應(yīng)用的入口點(diǎn)。 主進(jìn)程在 Node.js 環(huán)境中運(yùn)行,這意味著它能夠使用 所有 Node.js API。主進(jìn)程管理應(yīng)用窗口,處理窗口事件,與操作系統(tǒng)進(jìn)行交互控制原生桌面功能,例如菜單、對話框、托盤圖標(biāo)、發(fā)送桌面通知,但不進(jìn)行頁面渲染。簡單講主進(jìn)程主要做桌面原生功能開發(fā),可以調(diào)用node或者electron api進(jìn)行開發(fā)。
2.渲染進(jìn)程
Electron 為每個(gè)應(yīng)用窗口生成一個(gè)單獨(dú)的渲染器進(jìn)程,負(fù)責(zé)渲染窗口頁面內(nèi)容,渲染進(jìn)程頁面開發(fā)幾乎跟web項(xiàng)目開發(fā)一樣。在渲染進(jìn)程無法使用Node.js API。簡單講渲染進(jìn)程就是主要做web頁面開發(fā),無法使用node或者electron api。
3.預(yù)加載腳本
預(yù)加載腳本就是渲染進(jìn)程和主進(jìn)程通信橋梁,2端依靠預(yù)加載腳本進(jìn)行通信。預(yù)加載腳本在網(wǎng)頁加載到渲染器之前注入,類似于 Chrome 擴(kuò)展的 內(nèi)容腳本。使用場景例如在渲染進(jìn)程web頁面上需要調(diào)用原生桌面功能就得通過預(yù)加載腳本通知主進(jìn)程進(jìn)行功能實(shí)現(xiàn)。
4.進(jìn)程通信
4.1 send+on(單向)
渲染進(jìn)程向主進(jìn)程發(fā)送事件
例如實(shí)現(xiàn)下面場景:
渲染進(jìn)程向主進(jìn)程發(fā)送事件,調(diào)用原生功能發(fā)起桌面通知
預(yù)加載腳本通過 contextBridge API 定義 全局(window)對象屬性electronApi供渲染進(jìn)程調(diào)用(window.electronApi.notice())
preload.js(預(yù)加載腳本)
const { contextBridge,ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronApi', {
//notification事件名稱在主進(jìn)程注冊監(jiān)聽,content:傳遞值
notice:content=>ipcRenderer.send("notification",content)
})
渲染進(jìn)程調(diào)用
index.html
//發(fā)送事件給主進(jìn)程
window.electronApi.notice("你好")
主進(jìn)程監(jiān)聽事件
main.js
const { ipcMain,Notification } = require('electron')
ipcMain.on('notification', (event,content)=>{
//收到事件發(fā)送桌面通知,title為傳遞過來的值
new Notification({title:"來著渲染層消息",body:content}).show()
})
運(yùn)行結(jié)果:
4.2 invoke+handle (雙向)
除了用ipcRenderer.send +ipcMain.on進(jìn)行通信外還可用ipcRenderer.invoke+ipcMain.handle,區(qū)別在與send+on是單向消息無回調(diào),invoke+handle是雙向通信在主進(jìn)程處理完一些異步操作后可以給渲染層返回回調(diào),兩者用法基本類似
例如實(shí)現(xiàn)下面場景:
渲染進(jìn)程通知主進(jìn)程打開原生選擇文件功能并返回文件路徑
preload.js(預(yù)加載腳本)
const { contextBridge,ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronApi', {
//dialogOpenFile事件名稱在主進(jìn)程注冊監(jiān)聽
openFile:()=>ipcRenderer.invoke("dialogOpenFile")
})
渲染進(jìn)程調(diào)用
index.html
//發(fā)送事件給主進(jìn)程進(jìn)行文件選擇,返回選擇后文件路徑
const filePath=await window.electronApi.openFile()
console.log(filePath,"文件路徑")
主進(jìn)程監(jiān)聽事件
main.js
const { ipcMain,dialog} = require('electron')
ipcMain.handle('dialogOpenFile', ()=>{
const { canceled, filePaths } = await dialog.showOpenDialog()
if (!canceled) {
//返回文件路徑
return filePaths[0]
}
})
4.3 主進(jìn)程向渲染進(jìn)程發(fā)送事件
當(dāng)從主進(jìn)程向渲染器進(jìn)程發(fā)送消息時(shí),需要指定哪個(gè)渲染器正在接收該消息。 消息需要通過渲染器進(jìn)程的 WebContents 實(shí)例發(fā)送到渲染器進(jìn)程。 此 WebContents 實(shí)例包含一個(gè) send 方法,其使用方式與 ipcRenderer.send 相同可看成逆過程。
main.js
import { BrowserWindow } from 'electron'
const mainWindow= new BrowserWindow()//創(chuàng)建一個(gè)窗口
mainWindow.webContents.send("set-title","你好")
preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronApi', {
setTitle: (callback) => ipcRenderer.on('set-title', (event, value) => callback(value)),
})
index.html
window.electronAPI.setTitle((title) => {
console.log(title)//你好
})
三、窗口創(chuàng)建與應(yīng)用事件
應(yīng)用窗口是web頁面載體,類似瀏覽器標(biāo)簽一個(gè)窗口一個(gè)標(biāo)簽頁,需要打開多個(gè)標(biāo)簽就要?jiǎng)?chuàng)建多個(gè)窗口,像vue腳手架屬于單應(yīng)用項(xiàng)目就只需要一個(gè)窗口,應(yīng)用事件指的是一些生命周期或者窗口事件,比如應(yīng)用退出事件,窗口關(guān)閉事件等都有相應(yīng)的回調(diào)。
創(chuàng)建一個(gè)窗口示例:
const { BrowserWindow } = require('electron')
const path = require('node:path')
//創(chuàng)建一個(gè)窗口
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,//是否隱藏菜單欄
webPreferences:{//網(wǎng)頁功能設(shè)置
preload: path.join(__dirname, 'preload.js')//設(shè)置預(yù)加載腳本
},
//.....更多配置
})
//加載窗口html文件
win.loadFile('index.html')
}
- 通過new BrowserWindow(options)創(chuàng)建一個(gè)新窗口
- 通過loadFile(url)設(shè)置窗口對應(yīng)html文件進(jìn)行頁面渲染,其中url可以是本地html文件也可以是網(wǎng)址連接
- 通過webPreferences-preload屬性設(shè)置預(yù)加載腳本路徑,使窗口初始化時(shí)候運(yùn)行預(yù)加載腳本
在 Electron 中,瀏覽器窗口只能在 app 模塊的 ready 事件被觸發(fā)后創(chuàng)建。 你可以使用 app.whenReady() API 等待此事件
完整代碼:
const { app, BrowserWindow } = require('electron')
//只能在ready事件觸發(fā)后創(chuàng)建窗口
app.whenReady().then(() => {
createWindow()
})
//創(chuàng)建窗口
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
更多窗口配置可查看窗口配置屬性
應(yīng)用/窗口事件可以通過app.on(‘eventName’)注冊監(jiān)聽,app是electron內(nèi)置模塊可通過import { app } from 'electron’引入
例如:
//所有窗口關(guān)閉發(fā)出
app.on('window-all-closed', () => {
})
//當(dāng) Electron 完成初始化時(shí)發(fā)出一次
app.on('ready', () => {
})
//當(dāng)應(yīng)用被激活時(shí)發(fā)出
app.on('activate', () => {
})
//在應(yīng)用開始關(guān)閉其窗口之前發(fā)出
app.on('before-quit', () => {
})
還有非常多的事件,具體可查看app模塊
四、技術(shù)棧和構(gòu)建工具
上述介紹完electron核心概念和基礎(chǔ)用法后,下面將介紹electron結(jié)合web技術(shù)通過腳手架來開發(fā)項(xiàng)目
electron可以和任意web前端技術(shù)搭配使用,react或者vue2、vue3或者原生等。構(gòu)建工具可以選擇webpack或者vite??梢允謩?dòng)自由組合從零搭建或者使用一些網(wǎng)上分享的開發(fā)模板,傳統(tǒng)搭建方式都比較繁瑣。這里推薦一個(gè)新構(gòu)建工具electron-vite,它簡化了配置過程,支持主進(jìn)程、渲染進(jìn)程代碼快速熱更新,能自主選擇前端框架來搭建腳手架項(xiàng)目,提供了更快、更簡單的開發(fā)體驗(yàn)。
五、electron-vite安裝
1.環(huán)境要求
需要安裝node 18+版本
2.安裝
npm切換新淘寶源防止下載過程卡住
npm config set registry https://registry.npmmirror.com/
初始化項(xiàng)目
npm create @quick-start/electron
出現(xiàn)如下一些配置選項(xiàng)按需選擇:
-
Project name:項(xiàng)目名稱自定義輸入
-
Select a framework:選擇框架,內(nèi)置vanilla,vue,react,svelte,solid可選擇,演示這里我們選擇vue
-
Add TypeScript:是否添加TypeScript,演示這里我們選擇no
-
Add Electron updater plugin:是否添加Electron更新插件,yes
-
Enable Electron download mirror proxy:鏡像下載代理,國內(nèi)網(wǎng)絡(luò)建議開啟,yes
進(jìn)入項(xiàng)目目錄
cd electron-app
安裝依賴
npm install
修改支持主進(jìn)程熱更新
默認(rèn)只開啟了渲染進(jìn)程熱更新,需要在運(yùn)行命令后添加參數(shù)才能開啟主進(jìn)程熱更新, 打開package.json 文件,scripts-dev 命令后面添加 ‘--watch’
啟動(dòng)項(xiàng)目
npm run dev
桌面出現(xiàn)如下應(yīng)用窗口,項(xiàng)目啟動(dòng)成功!
ps:vue內(nèi)置框架默認(rèn)為vue3
3.快速安裝
你還可以通過附加的命令行選項(xiàng)直接指定項(xiàng)目名稱和你想要使用的模板。例如,要構(gòu)建一個(gè) Electron + Vue 項(xiàng)目,運(yùn)行:
# npm 6.x
npm create @quick-start/electron eletron-app --template vue
# npm 7+, extra double-dash is needed:
npm create @quick-start/electron electron-app -- --template vue
目前支持的模板預(yù)設(shè)如下:
JavaScript | TypeScript |
---|---|
vue | vanilla-ts |
react | react-ts |
svelte | svelte-ts |
solid | solid-ts |
4、目錄結(jié)構(gòu)
├── build // 編譯過程輸出文件目錄
├── dist // 打包后輸出目錄
├── node_modules // 依賴模塊
├── out //編譯過程輸出文件目錄
├── resources // 公共資源文件,主進(jìn)程使用
│ └── icon.png //默認(rèn)圖標(biāo)
├──src
│ ├── main // 主進(jìn)程開發(fā)目錄
│ │ └── index.js //主進(jìn)程入口文件
│ ├── preload // 預(yù)加載腳本開發(fā)目錄
│ │ └── index.js // 預(yù)加載默認(rèn)腳本
│ └── renderer // 渲染進(jìn)程開發(fā)目錄,類似純web項(xiàng)目根目錄
│ ├── src
│ │ ├── assets //資源文件目錄
│ │ ├── components //組件目錄
│ │ ├── App.vue // 入口頁面
│ │ └── main.js // 入口文件
│ └── index.js.html // 默認(rèn)html文件
├── .editorconfig
├── .eslintignore //eslint代碼檢查忽略配置文件
├── .eslintrc.cjs //eslint代碼檢查配置文件
├── .gitignore //git忽略配置文件
├── .npmrc // npm源配置文件
├── .prettierignore //prettier代碼格式化忽略配置文件
├── .prettierrc.yaml //prettier代碼格式化配置文件
├── dev-app-update.yml
├── electron-builder.yml //打包配置文件
├──electron.vite.config.mjs //electron-vite配置文件
├── package-lock.json
├── package.json
└──README.md //項(xiàng)目說明
我們需要重點(diǎn)關(guān)注的是src/main ,src/renderer兩個(gè)目錄,基本都在這2個(gè)目錄內(nèi)開發(fā)代碼,main為主進(jìn)程開發(fā)目錄放置主進(jìn)程相關(guān)文件,renderer為渲染進(jìn)程開發(fā)目錄,放置渲染進(jìn)程相關(guān)文件,renderer目錄其實(shí)可以當(dāng)成純web腳手架生成項(xiàng)目開發(fā),使用除了多了跟主進(jìn)程通信功能外其他和web腳手架項(xiàng)目一模一樣,包括目錄結(jié)構(gòu),第三方UI庫安裝(elmentui-plus等)、路由vue-router、vuex、pina等。
src/preload 為預(yù)加載腳本開發(fā)目錄,如無特殊需求我們可以不用管,打開src/preload/index.js 查看源碼:
import { contextBridge } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
} catch (error) {
console.error(error)
}
} else {
window.electron = electronAPI
window.api = api
}
可以看這句 contextBridge.exposeInMainWorld(‘electron’, electronAPI)
默認(rèn)已經(jīng)向渲染進(jìn)程全局注入了“electron”屬性,在渲染進(jìn)程打印下該屬性
console.log(window.electron,'electronAPI' )
發(fā)現(xiàn)注入了3個(gè)對象屬性ipcRenderer,process,webFrame,有了ipcRenderer意味著我們可以直接在渲染進(jìn)程發(fā)送事件給主進(jìn)程
App.vue
//給主進(jìn)程發(fā)送事件
window.electron.ipcRenderer.send("notice","hello ,來著app.vue頁面消息")
main/index.js
import { ipcMain } from 'electron'
ipcMain.on('notice',(event,msg)=>{
console.log(msg)//hello ,來著app.vue頁面消息
})
綜上所述,我們可以不用編寫預(yù)加載腳本,直接在web頁面通過暴露的electron.ipcRenderer進(jìn)行通信
5、公共資源和引用
公共目錄默認(rèn)為 根目錄/resources,專用于主進(jìn)程和預(yù)加載腳本。例如圖標(biāo)、可執(zhí)行程序、wasm 文件 等資源,可以將它們放在這個(gè)目錄中。
默認(rèn)情況渲染進(jìn)程公共資源單獨(dú)管理,在src/renderer新建public文件夾單獨(dú)存放使用。這跟web腳手架項(xiàng)目公共文件結(jié)構(gòu)和使用一致。
主進(jìn)程引用公共資源
在主進(jìn)程中,可以使用 ?asset 后綴將資源作為文件路徑導(dǎo)入
import { Tray, nativeImage } from 'electron'
import appIcon from '../../build/icon.ico?asset'
let tray = new Tray(nativeImage.createFromPath(appIcon))
在此示例中,appIcon 將解析為:
const path = require("path");
const appIcon = path.join(__dirname, "./chunks/icon-4363016c.ico");
在主進(jìn)程中文件路徑可以用node.js path模塊來拼接
- __dirname:表示當(dāng)前文件所屬目錄的絕對路徑
- path.join:將所有參數(shù)拼接成一個(gè)完整的路徑字符串
- path.resolve:將一些的 路徑/路徑段 解析為絕對路徑,類似cmd的cd命令功能,某些情況下跟path.join效果一樣,不加參數(shù)表示項(xiàng)目根目錄絕對路徑
- process.cwd():node.js進(jìn)程的當(dāng)前工作目錄,也即項(xiàng)目根目錄等價(jià)path.resolve()
舉例使用:
假設(shè)resources公共資源目錄有個(gè)test.txt文件
在主進(jìn)程/src/main/index.js讀取它
const path=require('path')
const filePath=path.join(process.resolve(),'/resources/test.txt')//文件路徑
或
const filePath=path.join(process.cwd(),'/resources/test.txt')//文件路徑
或
const filePath=path.join(__dirname,'../../resources/test.txt')//文件路徑
或
const filePath=path.resolve(__dirname,'../../resources/test.txt')//文件路徑
6、vue-router、pinia、scss、axios安裝
從目錄結(jié)構(gòu)看vite-electron只是生成一個(gè)最基礎(chǔ)的vue項(xiàng)目,vue全家桶都沒安裝,為了方便開發(fā)我們需要手動(dòng)安裝下,如果對這部份安裝很熟可以跳過。
vue-router安裝
npm install vue-router -S
renderer/src目錄下新建router文件夾,router文件夾內(nèi)新建一個(gè)index.js文件寫入:
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', redirect: '/home' },
{ path: '/home', component: () => import('../views/home/index.vue') }
],
})
export default router
renderer/src/main.js 引入
import router from './router'
const app= createApp(App);
app.use(router);
app.mount('#app')
renderer/src目錄下新建views文件夾,views文件夾內(nèi)新建home文件夾,home文件夾內(nèi)新建一個(gè)index.vue文件寫入:
<template>
home
</template>
<script setup>
</script scoped>
<style>
</style>
App.vue刪掉原來代碼寫入
<template>
<router-view></router-view>
</template>
<script setup>
</script>
<style>
</style>
pinia安裝
npm install pinia -S
renderer/src目錄下新建stores文件夾,stores文件夾內(nèi)新建一個(gè)index.js文件寫入:
import { defineStore } from 'pinia'
export const useIndexStore = defineStore('index', {
state:()=> {
return {
}
}, getters: {
},
actions: {},
//persist: true,//是否開啟持久化存儲(chǔ)
})
main.js引入
import { createPinia } from 'pinia'
const pinia =createPinia();
app.use(pinia);
scss安裝
vite-electron默認(rèn)已安裝了less,如果習(xí)慣用less可以不安裝scss
npm install sass -S
axios安裝
npm install axios -S
renderer/src目錄下新建utils文件夾,utils文件夾內(nèi)新建一個(gè)request.js文件寫入:
import axios from 'axios';
const baseURL = '/'
const http= axios.create({
baseURL,
timeout: 100000
})
// 請求攔截
http.interceptors.request.use(config=>{
return config
},error=>{
console.log(error);
})
// 響應(yīng)攔截
http.interceptors.response.use((res)=>{
return res.data
})
export default http
ps:攔截邏輯根據(jù)實(shí)際情況修改寫入,可以在renderer/src新建api文件夾統(tǒng)一維護(hù)接口
六、打包
1.應(yīng)用名稱修改
:
修改electron-builder.yml 文件內(nèi)字段productName
2.應(yīng)用圖標(biāo)更換
打包的應(yīng)用圖標(biāo)默認(rèn)路徑在build文件夾下icon.ico,必須是ico格式不能是png等其他格式,如果你的圖片格式是ico可以直接替換,打包生效,如果是其他格式需要轉(zhuǎn)換
安裝electron-icon-builder插件
electron-icon-builde能 把圖片轉(zhuǎn)換為ico格式
npm install electron-icon-builder -D
修改package.json,scripts新增命令行
"electron:generate-icons": "electron-icon-builder --input=./resources/icon.png --output=build --flatten"
把新圖片icon.png放置resources文件夾
執(zhí)行
npm run electron:generate-icons
可以看到在build目錄下生成了一個(gè)icons文件夾里面有ico格式圖片
可以直接復(fù)制icon.ico圖片到build目錄下替換,每次都要復(fù)制很麻煩我們修改下打包默認(rèn)的圖標(biāo)路徑:
打開electron-builder.yml
在nsis下添加
installerIcon: 'build/icons/icon.ico'
installerIcon屬性定義了打包圖標(biāo)的位置,重新打包生效。
3.打包
打包過程會(huì)去github上下載打包工具,國內(nèi)網(wǎng)絡(luò)大概率會(huì)卡住導(dǎo)致打包失敗,需要手動(dòng)下載放入指定位置才能順利打包,具體看下面說明
執(zhí)行打包命令:
window:
npm run build:win
mac:
npm run build:mac
linux:
npm run build:linux
接下來以打包window演示說明報(bào)錯(cuò)解決方法:
1.winCodeSign報(bào)錯(cuò)
把截圖上url后面的下載地址復(fù)制到瀏覽器下載,如果瀏覽器仍然下載不了,
用下面國內(nèi)源地址下載:
https://cdn.npm.taobao.org/dist/electron-builder-binaries/winCodeSign-2.6.0/winCodeSign-2.6.0.7z
注意下載版本號(hào)和報(bào)錯(cuò)提示的版本號(hào)要一樣,不一樣的自己修改下載連接:
https://cdn.npm.taobao.org/dist/electron-builder-binaries/ + 版本號(hào) + 文件名
下載后解壓到:
C:\Users\Administrator\AppData\Local\electron-builder\Cache\winCodeSign
ps:AppData是個(gè)隱藏文件,文件選項(xiàng)設(shè)置需要打開顯示隱藏文件功能才能找到目錄
2.nsis報(bào)錯(cuò)
同樣復(fù)制下載地址或者用國內(nèi)源到瀏覽器下載
國內(nèi)源:
https://cdn.npm.taobao.org/dist/electron-builder-binaries/nsis-3.0.4.1/nsis-3.0.4.1.7z
如果版本號(hào)不同一樣方法修改,下載解壓到:
C:\Users\Administrator\AppData\Local\electron-builder\Cache\nsis
3.nsis-resources報(bào)錯(cuò)
同樣復(fù)制下載地址或者用國內(nèi)源到瀏覽器下載
國內(nèi)源:
https://cdn.npm.taobao.org/dist/electron-builder-binaries/nsis-resources-3.4.1/nsis-resources-3.4.1.7z
下載解壓到
C:\Users\Administrator\AppData\Local\electron-builder\Cache\nsis
最終打包完成后會(huì)在dist文件夾下生成exe安裝包
4.應(yīng)用簽名
通過上面打包出來安裝包安裝會(huì)發(fā)現(xiàn)被安全軟件報(bào)有風(fēng)險(xiǎn),想繞過風(fēng)險(xiǎn)檢測應(yīng)用必須簽名,簽名只影響安全檢查不影響功能使用 ,簽名教程請移步官網(wǎng)查看:https://www.electronjs.org/zh/docs/latest/tutorial/code-signing
七. Electron API
原生桌面功能需要調(diào)用 Electron API,更多 Electron API請查閱官網(wǎng):https://electron.nodejs.cn/docs/latest/api/app文章來源:http://www.zghlxwxcb.cn/news/detail-798692.html
八. 總結(jié)
electron框架對web開發(fā)人員來說非常友好,無須了解原生開發(fā)技能,就能通過web技術(shù)進(jìn)行桌面應(yīng)用開發(fā),大大減少學(xué)習(xí)成本,一套代碼能快速構(gòu)建生成多端應(yīng)用,也大幅減少了開發(fā)成本。
簡言之,electron開發(fā)可以看成是桌面功能開發(fā)+純web頁面開發(fā),桌面功能開發(fā)在主進(jìn)程調(diào)用Electron API,而web頁面開發(fā)就是html,css,js技術(shù)棧。文章來源地址http://www.zghlxwxcb.cn/news/detail-798692.html
到了這里,關(guān)于electron桌面應(yīng)用開發(fā)——快速入門教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!