Electron:跨平台的視窗應用程式
歷史
很多事情沒有經歷過就無法深刻體會,所以”感同身受”真的很難,但是軟體工程師是一個很特別的職務,尤其是與業務的隔閡,說給同業的朋友聽時,既使他沒遇過同樣的事情,但是也可以感同身受了解你的苦處。
Q業務:『客戶需要一個 Windows 程式。』
- 攻城獅:『沒問題。』
- 躬成屍:GAME OVER
Q業務:『客戶的老闆是用 MacBook,所以系統要能支援 Mac,麻煩你加一下。』
- 攻城獅:『沒問題。』
- 躬成屍:GAME OVER
Q業務:『客戶的產線主管希望操作員也可以直接重機台的電腦上使用系統,不過他們是用 Linux,你再花點時間改一下就好。』
- 攻城獅:『沒問題。』
- 躬成屍:GAME OVER
Q業務:『產線有些嵌入式電腦(ARM),系統裝不起來,麻煩處理一下。』
- 攻城獅:『沒問題。』
- 躬成屍:GAME OVER
當事情發生在別人身上時那是喜劇,你可以安慰他”撐過去就是你的”;
當事情發生在自己身上時那就變成悲劇,你可能會…
Electron
在 Node.js 興盛起來後全端工程師(Full Stack Developer)的議題又再被炒熱起來,因為透過 Node.js 讓原本侷限在前端的 JavaScript 變成可以開發後端程式,這讓門檻瞬間降低很多。
Node.js 為 JavaScript 提供一些 API 來替 JavaScript 與作業系統甚至硬體設備溝通,開發過 Ionic 或是 Cordova 程式的人應該就會發覺這跟 Cordova 在做的事情幾乎一樣。
在 Cordova 架構下,它前面透過 WebView 來呈顯網頁,後面提供一些 API 來與系統或硬體界接,中間則是讓我們用 JavaScript 撰寫邏輯,往前可以與前端網頁互動,往後可以與系統或硬體溝通,整個架構就像:HTML(WebView) <=> JavaScript <=> API(Cordova) <=> OS、Hardware
那桌面系統是不是也有相同的方案?
我們開啟 Electron 官方網站 在網站內可以看到目前 Electron 最新版本為 1.7.6 版、Node 為 7.9.0 版、Chromium 為 58.0.3029.110 版、V8為 5.8.283.38 版,也就是說 1.7.6 版的 Electron 內含 7.9.0 版的 Node.js、58.0.3029.110 版的 Chromium,JavaScript 解析引擎為 5.8.283.38 版V8引擎。
比對 Cordova 架構就會變成:HTML(Chromium) <=> JavaScript <=> API(Node.js/V8) <=> OS、Hardware
前置準備
我們需要一個純前端的 Web 程式,這邊我們拿之前練習到 Angular 服務 的程式來當範例。
若要下載請記得先透過指令
npm install來重新安裝 package。
透過 Angular CLI 指令 ng build --prod 來建置專案,預設專案會輸出到專案目錄下的 dist 資料夾內。
安裝 electron
我們先建立一個資料夾 desktopApp,接著透過 npm init 來建立一個 package.json 檔,entry point 的設定值改成 main.js,因為這是 Electron 官方範本預設程式進入點。
接著安裝 electron,指令如下:npm install -d electron
開啟 package.json 並在 scripts 插入新指令 "start": "electron main.js"。
1 | { |
再來將剛才建置好的 Angular 專案(dist 資料夾)複製到 desktopApp 資料夾內。
main.js
開啟官方在 GitHub 的 electron-quick-start 專案,將 main.js 複製到 desktopApp 資料夾內。
執行 electron
我們透過 npm start 來啟動,它會執行剛才加入的指令 electron main.js,它會開啟一個 electron 程式 並自動載入 main.js。
不過目前畫面一片雪白,按 F12 也無法開啟開發人員工具,Electron 不是內含 Chromium 嗎?怎麼開不起來?
檢視 main.js,原來預設開發人員工具沒有開啟,取消註解,重新執行就可以看到開發人員工具。
雖然仍然沒畫面,但是從開發人員工具可以知道是起始頁面(index.html)的路徑錯誤造成的。

開啟 main.js,修正起始頁面的路徑,順便可以調整預設視窗大小,重新執行。
再次失敗畫面仍然沒出現,錯誤訊息顯示 index.html 內的 js 檔與 css 檔找不到,切換到 Elements 頁籤可以發現 index.html 確實被載入了,但是怎麼 js 檔與 css 檔找不到?

看一下存取路徑怎麼怪怪的。
開啟 dist\index.html 將 base tag 的 href 屬性改成 ./。
重新執行就正常了。
最後做一些 main.js 的設定微調。
關閉開發者工具:將 mainWindow.webContents.openDevTools() 註解起來。
隱藏主選單列:在 createWindow 方法內加入 electron.Menu.setApplicationMenu(null);。
1 | const electron = require('electron') |
重新執行,整體感覺已經跟一般桌面程式無異。
佈署 electron
最後當然是最重要的如何佈署到平台上,首先安裝 electron-packager 套件,指令如下:npm install -d electron-packager
electron-packager 的語法如下:electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...]
sourcedir:表示來源資料夾,.表示當前來源為資料夾。appname:輸出的執行檔名稱。--platform:表示要建置的平台,目前有:linux、win32、darwin,、mas、all。--arch:表示目前系統架構,選項有:ia32、x64、armv7l、arm64、all。其他參數可參考 官方文件。
Window 64位元版本
在 package.json 的 scripts 插入下列指令:electron-packager . WinApp --platform=win32 --arch=x64
執行 npm run build_win 來建置。
編譯後可以看到專案資料夾內多了一個 WinApp-win32-x64 資料夾,資料夾內有一隻 WinApp.exe,執行後就會看到與剛才相同的操作介面。

我們打開 WinApp-win32-x64\resources\app\ 資料夾其實可以發現程式都在裡面。
如果不希望別人可以輕易看到程式碼,可以加上 --asar 參數,讓 electron-packager 幫你封裝起來。"build_win": "electron-packager . WinApp --platform=win32 --arch=x64 --asar"
重新編譯後就可以看到我們的程式被封裝成 app.asar。
如果資料夾已經存在,請先移除,否則請加上
--overwrite參數來覆寫。
Linux 64位元版本
在 package.json 的 scripts 插入下列指令:electron-packager . LinuxApp --platform=linux --arch=x64 --asar,並透過指令 npm run build_linux 編譯。

將編譯所產生的 LinuxApp-linux-x64 資料夾複製至 Linux 64位元系統(筆者測試環境是 Ubuntu 17 64位元),並執行 LinuxApp,可以看到跟 Winodws 一樣的結果。
Linux ARM 版本
在 package.json 的 scripts 插入下列指令:electron-packager . ARMApp --platform=linux --arch=armv7l --asar,並透過指令 npm run build_arm 編譯。

將編譯所產生的 ARMApp-linux-armv7l 資料夾複製至 Linux ARM 系統(筆者測試環境硬體是 Raspberry Pi 3,系統是 Raspbian),並執行 ARMApp,可以看到跟 Winodws 一樣的結果。
因為筆者沒有 Mac 所以在此不做測試,但是方法應該大同小異。
最後我們來回想最前面的需求:客戶需要一種能在任何桌面系統上執行的程式。
在以往我們可能每個平台都需要額外學習很多技術來支援,甚至每種平台都需要專屬的開發人員,在時程上跟後續維護都會增加很多成本。
Electron 可以幫我們解決不少問題,當然它也有一些限制以及缺點,沒有最好的方案,只有最可行的辦法。
筆者以前主要用 C# 開發 Windows 程式,那時在客戶那 demo 完系統後,最怕遇到客戶說:『你們的系統能不能在瀏覽器上執行。』,現在真的有跨多平台需求時,我們可以對老闆說:『老闆,你找人來開發 Web 平台,剩下平台我一個人搞定。』。
