因為昨天在讀學徒模式,裡面提到了「建造可拋棄式玩具」這個模式,所以我就依循他的建議, 做了一個圈圈叉叉遊戲,目前只有弄出 Linux 和 Mac 版,Windows 版聽說比較麻煩, 所以等改天心情好了再做。
來講講這幾個小時的心境轉折以及遇到的困難。
Electron 的 Quick Start 真的超 Quick
超簡單,兩個指令就成功地跑起 electron app 了:
git clone https://github.com/atom/electron-quick-start
cd electron-quick-start && npm install && npm start
這時就會跳出一個包含有 chrome dev tool 的超陽春視窗了!
Electron 的簡單概念
Electron 非常方便,你可以用 HTML + CSS + JavaScript 建立出一個 Desktop Application, 也就是俗稱的桌面應用程式, 以前寫桌面程式要用 C/C++ 、Java 或是一些比較硬底子、不那麼善解人意的語言去寫, 現在只要會寫網站的工程師就能夠寫桌面程式哩!
那為什麼可以這樣呢? 其實概念很簡單,你可以看看自己的 Chrome, 他有分頁標籤、工具列… 之類的東西, 當你把這些東西拿掉,看起來就是個桌面程式哩! 這就是 Electrion 的最基本想法:
Electron 就是 chrome 拿掉工具列,拿掉分頁標籤,再載入 HTML 檔罷了。
大不了加些和系統對話的 API 給你用,其他基本上就是這個概念而已, 這也就是為什麼剛剛在試的時候會看到眼熟的東西的原因了。
開始動手!
圈叉遊戲其實也沒什麼,簡單寫一下我的架構:
<table>
<tr>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
</tr>
<tr>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
</tr>
<tr>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
<td>
<input type="checkbox">
<label for=""><div></div></label>
</td>
</tr>
</table>
簡單地說,就是一個 3 * 3 的表格,裡面放 div 而已~ 詳細的程式碼在文末有附上連結,可以自行參考。
寫的過程中有用到什麼資源?遇到什麼問題?
畢竟還是在寫程式,寫著寫著總是會用到一些資源, 這邊就記錄一下我在寫的過程中有用到的東西~
漸層色
因為我很懶得選顏色,所以就直接用 uigradients 的顏色去著色, 目前的顏色是隨便放的,改天心情好再去做修改。
判斷式
圈叉的勝利判斷有 8 種,說多不多,但說少也確實不少, 目前是用窮舉法,把八種情況都列出來, 心情好再去看看別人有沒有更漂亮的寫法。
不能用 forEach
這個我當時就傻眼,還一直用 [].forEach(()=>{})
確認他確實可以跑, 但我在寫的時候就真的不能用, 吃完飯後還是百思不得其解, 一查才發現原來 document.querySelectorAll
抓出來的元素並不是 array ,而是 NodeList… 於是我在 forEach 的前一行加上 NodeList.prototype.forEach = Array.prototype.forEach;
他就可以用了。
順便提一下,不要什麼東西都要用 jQuery 啊,如果只是因為要選元素,那可以直接用 document.querySelectorAll
, 用法就跟 jQuery 的 $(“CSS selector”) 一樣,像是:
var divs = document.querySelectorAll("#main table tr td div");
然後現在純 JS 也支援 classList 了,用法也很方便:
var div = document.querySelector("#yo");
div.classList.remove("blue");
div.classList.add("red");
這次的寫法原本有好幾個全域變數,後來才把他們都塞進 game 這個全域變數裡面, 之後要再把他用 IIFE 包起來,不過這樣好像就要加不少 listener 了… 之後再研究。
打包
一箱裝書一箱裝 CD,房間空出,心裡裝回憶。一邊偷哭一邊收行李,打包的工夫,AIN’T SO EASY。
—— 蛋堡《打包》
花了不少時間,終於把他寫完了,但總不能叫別人用我 APP 之前都要先 Build 一次吧, 又不是每個人都拿 Linux, 所以還是要找方法打包,原本以為是官方文件裡面的 Application Packaging 那一章節, 但我怎麼看都看不懂,照他說的依樣畫葫蘆還是一樣不知道生出來的 asar 檔可以幹嘛。
後來去找了一下以後發現原來是在 Application Distribution 之下,不過我努力地看了看,還是看不懂…
這時候心裡莫名有個聲音:「想要找到解答嗎?想要的話可以全部給你,去 Youtube 找吧!我把所有的答案都放在那裡了。」
搜尋了一下,找到了 Kyle Robinson Young 的 《Packaging and Distributing Electron Desktop Apps》。 裡面提到了一個工具:「electron-packager」,簡單地看了一下他的影片,就開始動手打包了, 因為過程實在太簡單,這邊就不贅述,簡單寫一下步驟就好:
- 安裝 electron-packager:
sudo npm i electron-packager -g
- 在 package.json 加入要打包的指令
electron-packager . --platform=linux --arch=x64 --version=0.36.5 Tic-Tac-Toe-Game
- 執行
npm run build
打包完了以後,我發現了一件事… Mac 版的包居然有 100MB,而 Linux 居然高達 400MB, 這真的太誇張了!!! 於是我就開始找資料、問人,後來在亂翻 build 出來的資料夾時,發現了 node_modules 這個資料夾, 想想好像不用把他加進來,就手動刪除,程式還是可以跑! 所以我就把他刪掉啦!
electron-packager 好像有個 –ignore=“要忽略的檔案路徑” 選項可以用, 但我用了半天用不出來… 有空再來研究要怎麼讓他自動消失, 最糟的情況就是在 build 的指令裡面多寫個 && rm /path/to/folders
而已。真怪,明明就有支援了,為什麼不能用呢…?
後記 & 結論
「建立可拋棄玩具」確實有幫助! 在短短幾個小時內就學到了個新東西, 雖然之前就有玩過, 但總是玩個兩天還是沒半點成果, 今次有建立起一個小玩具了,雖然沒有什麼太高深的技術, 但至少讓我更熟悉這個我以後說不定會用到的技能了。
優點
方便!只要用前端的三個語言來寫, 內容架構用 HTML,樣式風格用 CSS,互動用 JavaScript, 寫桌面程式就跟寫網頁一樣,還可以跨平台! 而且還可以搭配一堆框架什麼的 react.js、Angular.js 只要是寫網頁的資源, 用了 Electron 就可以變成寫桌面程式的資源啦!
缺點
缺點也很明顯,Build 出來的成品檔案大小太龐大。
此外,可以預期使用 electron 建立的應用程式所耗用的記憶體應該也會非常多。就像一開始說的,electron 就是 Chrome 拿掉不重要的東西再載入 HTML 檔, 也就是說,可以把 Electron App 當作是一個 Chrome。所以如果電腦裡有 10 個這種 App,那就等於你電腦裡同時有十個 Chrome 在運作… 光聽起來好恐怖⋯
參考資料
- Electron 官網: http://electron.atom.io
- Kyle Robinson Young 的 Packaging and Distributing Electron Desktop Apps: https://www.youtube.com/watch?v=dz5SnmBzBXc
- Electron-packager 的 npm 頁面:https://www.npmjs.com/package/electron-packager
- 圈叉遊戲的 source code:https://github.com/wi1d5ky/Tic-Tac-Toe-Game
- 討論 document.querySelectorAll 的 forEach 文章:https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/