目前社區兩大 Vue Electron 的腳手架:electron-vue 和 vue-cli-plugin-electron-builder。都有這樣那樣的問題,且都還不支持 Vue3,然而 Vue3 已是大勢所趨,Vite 勢必也將成為官方 Vue 腳手架,下圖是尤雨溪在開發好 Vite 之后與 webpack 之父的對話:
所以開發一個 Vite Vue3 Electron 的腳手架的需求日趨強烈。
我前段時間做了一個,但是發現了一些與 Vite 有關的問題,比如:Vite 會把開發環境的 process 對象吃掉的問題。
這對于 web 項目來說問題不大,但對于我們的 Electron 項目來說,就影響很大了。
今天我就把這個思路和實現方式的關鍵代碼發出來供大家參考,同時也希望 Vue 社區的貢獻者們,能注意到這個問題(給 Vue 官方的各個項目提 issue 真的是太難了,Electron 官方項目在這方面就做的很好,很 open、很包容)。
環境
先用 Vite 創建一個 Vue3 的工程,這就是你的實際項目工程。接著安裝幾個 Electron 相關的依賴,最終我的工程下的依賴情況如下:
注意:這些依賴全部安裝在 devDependencies 下
各個庫的版本發文時應該是最新的了,不過如果有更新的版本,你完全可以用,沒影響。工程的目錄結構大概是如下這樣:
接著在 package.json 中,增加兩個命令:
同時在 script 目錄下創建相應的文件,接著我們就開始撰寫者兩個文件的代碼了
調試腳本
通過 Vite 啟動 Web 項目
調試腳本首先要做的工作就是啟動 Vue 項目,讓它跑在 http://localhost 下,這樣我們修改渲染進程的代碼時,會通過 Vite 的熱更新機制實時反饋到界面上。
Vite 除了提供 cli 的指令啟動項目外,也提供了 API,我這里就是直接調它的 API 來啟動項目的,關鍵代碼如下:
其中 this.serverPort 是綁定在當前對象上的一個變量,意義是指定 vite 項目啟動時使用的端口號。啟動成功后 http server 對象綁定到當前對象的 server 變量上,如果啟動過程中報錯,則很有可能是端口占用,將執行如下邏輯:
這段邏輯就是遞增端口號,再次嘗試啟動 http server。
設置環境變量
往往每個開發人員的環境變量都是不一樣的,有的開發人員需要連開發服務器 A,有的開發人員需要連開發服務器 B,而且開發環境的環境變量、測試環境、生產環境的環境變量也不一樣,所以我把環境變量設置到幾個單獨的文件中,方便區分不同的環境,也方便 gitignore,避免不同開發人員的環境變量互相沖突。
開發環境的環境變量保存在 src/script/dev.env.js 中:
生產環境的環境變量則為 release.env.js。這個文件的代碼非常簡單,如下:
需要注意的是:ELECTRON_DISABLE_SECURITY_WARNINGS。這個環境變量是為了屏蔽 Electron 開發者調試工具那一大堆警告的(你如果開發過 Electron 應用,你應該知道我說的是什么),APP_VERSION 是從項目的 package.json 中取的版本號,你當然可以不設置這個環境變量,通過 Electron 的 API 獲取版本號:
但通過 ElectronAPI 獲取到的版本號,在開發環境下,是 Electron.exe 的版本號,不是你的項目的版本號,打包編譯后,這個問題是不存在的。
ENV_NOW 是當前的環境,開發環境下它的值為 dev,打包編譯后的生產環境它的值應為 product,因為現在我們是講如何構建開發環境,引用的是 dev.env.js,等下一篇文章講如何構建編譯環境時,引用的就是 release.env.js 了。
編譯主進程代碼
Vite 之所以快,有一個很重要的原因是它使用了 esbuild 模塊來編譯代碼,這里我們也使用 esbuild 來編譯我們的主進程的代碼。前面說了主進程是放在 src/main/ 目錄下的,這里我使用的是 TypeScript 開發,入口程式是 app.ts,你完全可以使用 Js 開發,文件名也隨你自定義:
esbuild 會自動查找 app.ts 引用的其他代碼,還有 treeshaking 機制保證你不會把無用的代碼打包到輸出目錄。我把 sourcemap 關掉了,因為調試主進程很困難,基本都是手動 console.log 資訊調試的,朋友們有好的建議請賜教一下。platform 要指定成 node,要不然 esbuild 會嘗試幫你去找 node.js 內置的包,肯定找不到,就報錯了。
同理,還要把 electron 設置成 external,在上一節設置的環境變量的基礎上,我們又增加了一個 WEB_PORT 的環境變量,Electron 啟動后,要根據這個變量去加載 localhost 的頁面,這個變量是應用啟動時確定的,是動態的,所以沒辦法設置到 dev.env.js 中,輸出代碼前,我們把環境變量的值也附加在輸出代碼中了。
這樣 Electron 進程啟動時,會先設置好環境變量,再執行具體的業務代碼(我們當然也可以通過其他方式設置環境變量,但這樣做主要是為了和生產環境保持一致,看到下一篇文章你就會知道了),最終生成的代碼會被輸出到這個目錄下面:
稍后我們啟動 Electron 時,也會讓 Electron 加載這個目錄下的入口程式。
啟動 Electron
Electron 的 node module 并沒有提供 API 給開發者調用以啟動進程,所以我們只能通過 node 的 child_process 模塊來啟動 Electron 的進程,代碼如下:
require(“electron”).toString() 得到的是 Electron 的可執行文件的路徑:
-
Windows 環境下為:node_modules\electron\dist\electron.exe
-
Mac 環境下為:node_modules/electron/dist/Electron.app/Contents/MacOS/Electron
path.join(this.bundledDir, “entry.js”) 為 Electron 進程指定了入口程式文件的地址,cwd: process.cwd() 是為 Electron 指定當前工作目錄(此處又為 Electron 指定了一次環境變量,其實不指定也沒關系),當 Electron 進程退出時,我們也關閉了 Vite 創建的 http server。
主進程加載渲染進程頁面
此處最關鍵的邏輯就是這一句:
process.env.WEB_PORT 就是我們上文中設置的 WEB_PORT 變量。
這個邏輯當然還有 else 分支,那是下一篇博文的內容了。