Electron 是一個 js 桌面端框架,讓 html,js 打包為桌面應用成為可能,已經有非常多的應用使用了這門技術,如:vscode,notion,figma,思源筆記等等
一 整體交互形式#
桌面應用最重要的是什麼,或者說和瀏覽器頁面有什麼區別?大致來說就是可以和操作系統交互,那麼什麼叫可以和操作系統交互?讓我們回顧一下瀏覽器,瀏覽器就像一個盒子,裡面的所有頁面的權利是有限的,就比如,
- 我想在瀏覽器自己寫的 html 頁面通過 js 函數,在文檔這個文件夾下創建一個文件,或者修改一個文件
- 調用系統通知 api,使用系統通知
- 修改系統音量?
這些都是做不到的,這些都涉及了操作系統的 api,那 electron 是如何做到的呢,我們看一下這個圖:
Electron 就相當於一個中間層,為 js 提供了一個曲線訪問系統 api 的能力,做任何和系統有關的操作,都要通過 electron
一個簡單的例子,我想通過文件選擇器,拿到一個文件的路徑,該如何實現?你可能會想到,瀏覽器也可以呀,通過選擇器,選擇一個文件,還要 Electron 幹嘛?
是沒問題,可以選到一個文件,但是文件的路徑是獲取不到的,簡單思考一下,如果一個普通的網頁,點擊就能獲取到你本機文件的路徑,甚至別的操作系統信息,你作為瀏覽器的開發者會允許這樣嗎?這樣對瀏覽器使用者來講顯然是非常危險的,點開個網頁信息洩密了,甚至電腦上文件丟了。
因此,我們要使用的文件選擇器是操作系統的文件選擇器,這個顯然是 js 辦不到的,因此有了 Electron,它提供了這樣的 api:文檔詳見:https://www.electronjs.org/zh/docs/latest/api/dialog
import {dialog} from "electron";
function open() {
let files = dialog.showOpenDialogSync({
title: '選擇文件路徑',
properties: ['openDirectory', 'multiSelections']
})
console.log(files)
return files
}
文件選擇器有了,我們該如何通知 Electron 呢?換句話說就是,上面的 js 函數是顯示層調用不到的,因此 Electron 提供了一種方式,讓我們可以與它交互。這種方式類似 js 的發布訂閱模型,發布一個事件,消費者監聽事件。
文檔詳見:https://www.electronjs.org/zh/docs/latest/api/ipc-renderer
js 端通過 ipcRenderer.send 發送一個事件通過 Electron
const electron = window.electron
electron.ipcRenderer.send('事件名稱',data)
electron 端通過 ipcMain.on 監聽 js 端發送過來的事件,那當這件事情處理完成了,想要告訴 js 端怎麼辦?通過 event.reply 發送一個事件,攜帶數據給 js,和上面的 ipcRenderer.send 是類似的
import {ipcMain} from "electron"
ipcMain.on('事件名稱',(event, data)) => {
//處理
//回調
event.reply('新的事件名稱', 要返回的數據)
})
js 端該如何處理呢?以 React 為例:useEffect 中,通過 ipcRenderer.on,監聽這個事件,然後處理,一定要在 [] 這個 useEffect,代表頁面創建就運行這個函數,監聽這個事件,在 return 中刪除這個事件,不然每次每次頁面加載都會創建一個監聽,這樣會越建越多
useEffect(() => {
//處理Electron端發送的事件
electron.ipcRenderer.on('事件名稱', (event, 數據) => {
//處理
});
return () => {
electron.ipcRenderer.removeAllListeners('事件名稱');
};
}, []);
到這裡就完成了一個交互,是不是很簡單。
二 打包#
打包全靠配置,這個是我的配置:
/**
* @see https://www.electron.build/configuration/configuration
*/
{
appId: "electron-music",
productName: "electron-music",
copyright: "Copyright © 2023 ${author}",
asar: true,
directories: {
output: "release",
buildResources: "public"
},
files: [
"dist"
],
win: {
icon: "public/icons/music256x256.png",
target: [
{
target: "dir",
arch: [
"x64"
]
},
{
target: "nsis",
arch: [
"x64"
]
}
]
},
nsis: {
"oneClick": false,
// 創建一鍵安裝程序還是輔助安裝程序(默認是一鍵安裝)
"allowElevation": true,
// 是否允許請求提升,如果為false,則用戶必須使用提升的權限重新啟動安裝程序 (僅作用於輔助安裝程序)
"allowToChangeInstallationDirectory": true,
// 是否允許修改安裝目錄 (僅作用於輔助安裝程序)
"installerIcon": "public/icons/music256x256.png",
// 安裝程序圖標的路徑
"uninstallerIcon": "public/icons/music256x256.png",
// 卸載程序圖標的路徑
"installerHeader": "public/icons/music256x256.png",
// 安裝時頭部圖片路徑(僅作用於輔助安裝程序)
"installerHeaderIcon": "public/icons/music256x256.png",
// 安裝時標題圖標(進度條上方)的路徑(僅作用於一鍵安裝程序)
"installerSidebar": "public/icons/music256x256.png",
// 安裝完畢界面圖片的路徑,(僅作用於輔助安裝程序)
"uninstallerSidebar": "public/icons/music256x256.png",
// 開始卸載界面圖片的路徑(僅作用於輔助安裝程序)
"uninstallDisplayName": "electron-music-${version}",
// 控制面板中的卸載程序顯示名稱
"createDesktopShortcut": true,
// 是否創建桌面快捷方式
"createStartMenuShortcut": true,
// 是否創建開始菜單快捷方式
"include": "script/installer.nsi",
// NSIS包含定制安裝程序腳本的路徑,安裝過程中自行調用 (可用於寫入註冊表 開機自啟動等操作)
"script": "script/installer.nsi",
// 用於自定義安裝程序的NSIS腳本的路徑
"deleteAppDataOnUninstall": true,
// 是否在卸載時刪除應用程序數據(僅作用於一鍵安裝程序)
"runAfterFinish": false,
// 完成後是否運行已安裝的應用程序(對於輔助安裝程序,應刪除相應的復選框)
"menuCategory": false,
// 是否為開始菜單快捷方式和程序文件目錄創建子菜單,如果為true,則使用公司名稱
"perMachine": true,
//給機器上所有用戶安裝
"language": "2052"
//安裝語言(中文)
},
mac: {
icon: 'public/icons/music256x256.png',
category: 'Productivity',
target: [
{
target: 'default',
arch: [
'arm64',
'x64'
]
}
]
},
linux: {
icon: "public/icons/music256x256.png",
target: [
"AppImage",
"tar.gz"
],
"category": "Audio",
artifactName: "${productName}-Linux-${version}.${ext}"
}
}
參考: 我的音樂播放器項目
三 注意事項#
1 在 linux 下窗口關閉前做一件事#
看環境,比如 kde 下是做不到比如隱藏窗口的,在 kde 下,點擊關閉按鈕,window 對象對直接就銷毀了。(曲線方式,我們可以不用系統自帶的頂欄,自己實現一個,這樣就不會有這樣的問題了)
app.on('closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
2 linux 下,英偉達顯卡涉及到動畫的頁面報錯(暫未解決)#
報錯內容
libva error: vaGetDriverNameByIndex() failed with unknown libva error, driver_name = (null)