Web 應用程式的跨平台運作特性,減低了一些應用開發的負擔。但是隨著多樣化的移動設備應用,Web 應用程式存在一些限制和挑戰,例如,
- 網路不是一直可用的。當你在火車上或者在隧道裡的時候,就可能沒有網路,你的 Web 應用也會因此中斷,這對於使用者來說可能造成非常多的困擾。
- Web 流覽器本身也都有一些安全策略,因此對於本機的軟硬體資源也都有限制。
基於這些理由,原生的桌面應用(Desktop)需求也有其存在的優勢,但是開發這些原生應用需要精通不同的技術,進入的門檻複雜。因此過去有類似 Apache Cordova 這類的開發平台,應用現有的 Web 開發技術來開發移動設備的原生應用。最近 的 PWA 架構,也在試圖使用 Web 技術,來取得像原生應用的優點。
JavaScript 生態圈則有 NW.js 與 Electron,試圖將現有的 Web 技術應用在桌面應用系統(Desktop Application)上,寫一份程式碼就可以在 Mac、Windows 及 Linux 上建置及執行。
NW.js 和 Electron
NW.js 是 Node.js 第一個桌面應用開發框架,但是近年來 Electron 的發展比較快速,風頭蓋過了 NW.js。兩者都是來自於同一個創造者,但是兩者在內部架構上採用不一樣的策略。以流行度與發展趨勢來看,Electron 似乎領先 NW.js。不過也有人更喜歡 NW.js,因為相對而言,NW.js 在代碼運行和應用加載方面比較簡單,而且它還支援像 Google 的 Chromebook 平台。
NW.js
簡單來說,NW.js 是一個框架,它支持用 HTML、CSS 和 JavaScript 來構建桌面應用(Desktop Application)。它整合 Node.js 與瀏覽器中的 WebKit 瀏覽器引擎,因此開始的命名為 node-webkit。
通過整合 Node.js 與 WebKit,不僅可以在應用視窗內載入 HTML、CSS 與 JavaScript 文件,還可以通過 JavaScript API 和作業系統進行互動。通過這個 JavaScript API 可以控制視窗的視覺元素,例如,視窗大小
、工具條以及菜單項目,而且還可以訪問本機文件系統與其它資源,這些都是 Web 應用無法做到的。
我們還是由 Hello Tainan 開始。
NW.js Hello Tainan
首先先要安裝 NW.js,開啟一個終端視窗,使用本機管理者身份將 nw 安裝成全局(global)套件,這樣每個專案就可以共享。
1 | npm install -g nw |
這個安裝會花一些時間!我大該花了 10 分鐘。
新建一個資料夾 hello-tainan-nwjs,我們要在此建立一個 NW.js 專案。開啟 VSCode 選擇專案資料夾,然後開啟一個終端視窗,初始化專案:
1 | npm init -y |
在專案目錄下新建一個子目錄 app,我們會將程式碼放在這個目錄中,在這個 app 目錄下新建一個 index.html 文件。
1 |
|
在 app 目錄下再開一個子目錄 javascripts 來擺放 JavaScript 程式檔,在 javascripts 目錄下建立 main.js 程式檔,我們會將主要的程式碼放在這個文件中,目前裡面只是一個簡單的 sayHello( ) 函數。
1 | function sayHello () { |
修改專案目錄下的 package.json 的 main 屬性,將其值指向 “app/index.html”,這會是我們專案的入口點:
1 | { |
從專案目錄下開啟終端視窗啟動 NW.js
1 | nw . |
你將會看到如下的樣子。
如果單擊螢幕上的 Say Hello 按鈕,會彈出一個寫著 “Hello Tainan, 台南!” 的警示視窗。
如果從 Chrome 流覽器直接打開 index.html 文件檔,也會看到同樣的界面,單擊按鈕也會看到同樣的結果。
這就是關鍵,代碼不需要修改,你就可以直接將網站的 HTML 頁面轉為 NW.js 開發的桌面應用。
我們來上一點妝扮,在專案目錄下開啟一個終端視窗安裝 bootstrap。
1 | npm install bootstrap --save |
從 node_modules/bootstrap/dist/css/ 將 bootstrap.min.css 複製到 app/stylesheets 目錄下。並將 bootstrap.min.css 加入 index.html,並在一些 DOM 元素加上一些 bootstrap 的妝扮。
1 |
|
將 Vue.js 也加進來:
1 | npm install vue --save |
將 node_modules/vue/dist 下的 vue.min.js 複製到 app/javascripts 目錄下, 將 vue.min.js 加入 index.html,再來就需要修改 main.js 產生 Vue 實例,我們也會透過 REST API 從遠端抓取一些資料。
1 | <body> |
修改 main.js 讓它多作點事情:
1 | function sayHello () { |
啟動 NW.js,另外也從瀏覽器直接打開 index.html 檔。
背景是從流覽器執行的畫面,前景則是 NW.js。兩者的程式碼都來自同一份,完全沒甚麼不同!
Electron
Electron 和 NW.js 的區別之一就是整合 Chromium 和 Node.js 的方式不同。NW.js 維護一個共享的 JavaScript 上下文,而 Electron 有多個獨立的 JavaScript 上下文,一個負責啟動運行的視窗 (main),另外一個負責具體的應用視窗 (renderer)。所以在啟動時也有很大的區別,NW.js 通常使用 HTML 作為入口文件,而 Electron 使用的是 JavaScript 文件。
Electron Hello Tainan
一樣要先安裝 Electron,開啟一個終端視窗,使用本機管理者身份:
1 | npm install -g electron |
這也會花一些時間。
新建一個資料夾 hello-tainan-electron,我們要在此建立一個 Electron 專案。開啟 VSCode 選擇專案資料夾,然後開啟一個終端機視窗,初始化專案,除此之外,專案本身也需安裝 electron,這也會花一些時間:
1 | npm init -y |
在專案目錄下新建一個子目錄 app,我們會將程式碼放在這個目錄中,在這個 app 目錄下新建一個 index.html 檔與 main.js 程式碼文件。在 app 目錄下另外開兩個子目錄 javascripts 與 stylesheets。
以下則是 app 目錄下的 index.html 與 main.js 文件檔。
1 |
|
1 | const { app, BrowserWindow } = require('electron'); |
main.js 是 Electron 的入口點,使用 mainWindow.loadURL( ) 將 index.html 加載進來,這個 index.html 與 NW.js 的 index.html 內容是一樣的。
再來要加入我們自己的程式碼,新建一個 JavaScript 文件 app/javascripts/app.js 放我們的應用程式碼,目前只有一個函數 sayHello( )。
1 | function sayHello() { |
接下來一樣要修改專案目錄下的 package.json 的 main 屬性,更改專案的入口點,這與 NW.js 完全不同,是 JavaScript 文件:
1 | { |
從專案目錄下開啟終端視窗啟動 Electron
1 | electron . |
你將會看到如下的樣子。
如果單擊螢幕上的 Say Hello 按鈕,會彈出一個寫著 “Hello Tainan, 台南!” 的警示視窗。
如果從 Chrome 流覽器直接打開 index.html 文件檔,也會看到同樣的界面,單擊按鈕也會看到同樣的結果。
同樣,把 bootstrap 與 Vue.js 加進來。
1 | npm install bootstrap --save |
分別將 node_modules/bootstrap/dist/css/bootstrap.min.css 與 node_modules/vue/dist/vue.min.js 複製到 app/stylesheets 與 app/javascripts 目錄
修改 app/index.html 與 app/javascripts/app.js:
1 | <body> |
1 | function sayHello() { |
啟動 Electron
1 | electron . |
另外也從瀏覽器直接打開 index.html 檔。
背景是從流覽器執行的畫面,前景則是 Electron。兩者的程式碼也都來自同一份,完全沒甚麼不同!
我們常用的 Visual Studio Code 程式碼編輯器就是使用 Electron 開發的。
微軟 Metro UI
隨著 Window 8 和 Surface 平板的發佈, 微軟對其 UI 做了很大的改變,引入了一套新設計風格的 UI,名為 Metro。帶來的改變不僅是重新設計了元素的樣式,還包括了應用佈局和結構的改變。有人基於這種 Metro 風格的規範,做了一個名為 Metro UI CSS 的 CSS 框架,它可以讓開發者建出符合 Metro 規範的基於 HTML 的應用樣式。
將前面範例使用的 bootstrap 用 Metro 取代,可做出像微軟桌面應用系統風格的畫面:
Offline First 本機存儲
最後我們要建立一個極簡單的備忘便條應用,就像可以完全離線操作的桌面應用系統。我們這裡使用較簡單的 NW.js 框架。
首先建立專案資料夾 let-me-remember,然後切換到專案目錄下,初始化一個 npm 專案。
1 | cd let-me-remember |
我們需要修改 package.json 來啟動應用程式,並定義起始視窗的大小。
1 | { |
這裡我們將應用程式的入口點 main 屬性改為 app/index.html,並新增了 window 屬性,定義了起始視窗的大小,也去除了視窗的邊框,讓它看起來就像螢幕上的便條簽。scripts 新增了 start 屬性,這樣我們可以使用 npm 啟動專案。
在專案目錄下建一子目錄 app 來放我們的程式碼,並在 app 目錄下再建兩個子目錄 stylesheets 與 javascripts 來擺放 CSS 與 JavaScript 文件。首先是 app 目錄下的 index.html 文件。
1 |
|
HTML 文件很簡單,使用 textarea 元素來存放用戶輸入的便條內容。還用了一個 id 為 close 的 div 元素,當它被單擊的時候,會觸發退出應用的流程。注意這裡,我們可以從 HTML 中的 JavaScript 上下文中直接調用 Node.js 的 process 全局變數。
另外我們要新增 app.css 與 app.js 兩個文件,這分別位於 stylesheets 與 javascripts 目錄下。
1 | body { |
1 | function initialize() { |
我們使用 HTML5 的 localStorage API,可以在應用中實現數據持久化,並且可以讓保存數據發生在後台,用戶完全感覺不到。
現在可以啟動應用程式了。
1 | npm start |
畫面看起來就像直接貼在螢幕上的便條籤。隨意打入一些備忘內容,內容就會被保存。當重新打開應用時,此前保存的內容會顯示在便條中。
最後我們需要將此應用直接打包成桌面應用的可執行檔。我們要使用一個 nw-builder 的套件,首先安裝它,如果要安裝成全局套件需要系統管理者權限。
1 | npm install nw-builder -g |
目前 nw-builder 可以打包的平台有 win32, win64, osx32, osx64, linux32, linux64,這裡我們要打包成
win64 的應用。
1 | nwbuild . -o ./build -p win64 |
第一次執行 nwbuild 會花一些時間,它需要從網路分別下載不同平台的打包工具 nwjs sdk 來執行打包的工作。執行完後會在專案目錄下產生一個 build 目錄,目錄下會有一個專案名稱的子目錄,裡面又會分不同的作業系統目錄。我們可以在 win64 目錄下找到 Windows 版本的 let-me-remember.ext 文件,現在只要直接打開此執行檔,就可以啟動我們的應用程式了。
Electron 版本的備忘便條應用就自己試試了。