:::

用HTML5開發網頁APP、桌面APP、跟行動裝置APP:混合應用程式框架 / Hybird APP Framework for Web APP, Desktop APP and Mobile APP

image

HTML5 (JavaScript + CSS3)可以在網頁上跨平臺執行已經是眾所皆知的常理,但是要如何用HTML5來開發能在瀏覽器上執行、能夠在桌面作業系統上安裝、還能夠在行動裝置(手機、平板)上安裝的應用程式呢?這個OnsenUI+Vue.js+Electron+PhoneGap Build的「混合應用程式框架」就是我的答案。

因為這只是最近心血來潮整理出來的混合做法,架構上並不是很漂亮,我也沒有打算寫太多文件。但對於想要發揮HTML5極限的開發者來說,應該是個不錯的參考。


程式碼 / Source code

image

混合應用程式框架保存在GitHub中,任何人都可以自由下載、修改、發佈。

軟體展示 / Demo

在混合應用程式框架中,我們只要開發一個HTML5框架,就可讓它以在網頁瀏覽器、桌面端應用程式、行動端應用程式執行。你可以實際玩玩看,看看混合應用程式框架會是什麼樣子:

接下來我用以下幾張截圖跟照片來讓大家看看它在不同平臺上的樣子:

網頁瀏覽器截圖 / Run in Web Browser

image

這是在Chrome網頁瀏覽器上的畫面。

Windows桌面版應用程式截圖 / Run in Windows

2018-05-24_153932

這是在Windows桌面端執行的畫面。

Android手機應用程式截圖 / Run in Android Phone

Screenshot_20180524-164945

Screenshot_20180524-164949

這是在Android手機上執行的畫面。

Android平板應用程式截圖 / Run in Android Tablet

Screenshot_20180524-165216

Screenshot_20180524-165208

這個是在Android平板 (8吋)上執行的畫面。

除了一些元件的位置怪怪的之外,大致上畫面都能夠正常呈現,很不錯吧。

接下來就讓我們來看看混合應用程式框架怎麼做到這件事情的吧。


框架架構 / Framework architecture

image

我把「混合應用程式框架」的架構整理成上面四個層次:顯示畫面 (Display)、平臺轉接(Platform Transport)、裝置(Client Device)、作業系統(Client Operation System, OS)。其中藍色的部分是我們可以掌握的元件,而灰色的部分對應不同使用者會用的客戶端。以下說明顯示畫面跟平臺轉接的細節。

顯示畫面 / Display

2018-05-24_153932

這邊使用的是Onsen UI,一個成熟的HTML5顯示框架。而控制資料邏輯的MVVM架構使用的是Vue 2。

我已經在「政大借書籃」跟「Voc4Fun全民樂單字」使用Onsen UI框架,但那時候Onsen UI只有跟Angular.js整合。現在Onsen UI已經開發到第二版,而且可以跟jQuery、Vue 2整合。

站在一個想要追求快速開發的角度,Vue算是相當好上手的MVVM架構。而這裡我選擇的是不編譯的Vue架構,因此可以直接在瀏覽器上執行,不需要任何打包或封裝。

平臺轉接 / Platform Transport

混合應用程式框架基本上可以直接用網頁瀏覽器開啟,但如果要打包成不同裝置上可以安裝的應用程式(APP),我們需要藉助一些平臺轉接的框架。

轉接桌面端應用程式:Electron / Electron for Desktop Application

Electron_0.36.4_Icon

(圖片來源:維基百科)

為了產生可以在桌面端作業系統安裝的應用程式,我們使用的是GitHub開發的知名框架Electron。

Electron可以產生給Windows、Mac OS、Linux使用的應用程式。這邊我主要只用Windows,Electron會在Windows平臺中產生可執行檔案,我寫了AutoIT將設定包裹成exe檔案,讓使用者可以直接點選執行。未來也可以更進一步使用InnoSetup打包成安裝檔。

至於Linux跟Mac OS我則沒有測試過,Linux可以直接用Electron打包,但我沒試過能不能執行。至於Mac OS,則必須要在Mac環境下打包才能使用,因此我也沒有試過。

之前我已經在「試做Electron桌面應用程式:webapp-wrapper」整理了一個簡單的基礎,現在我把它整合到混合應用程式框架中,讓Electron更容易將HTML5打包成桌面端應用程式。

轉接行動端應用程式:PhoneGap Build / PhoneGap Build for Mobile Application

Phonegap-build-mobile-app-development-tool-300x245

(圖片來源:boston technology)

為了產生可以安裝在行動裝置上的應用程式,包括手機、平板,我們使用的是PhoneGap Build。

PhoneGap Build提供了將HTML5程式打包成Android、iOS、Windows Phone三種平臺APP的功能。不過因為建構iOS APP需要具備開發者身份(每年需繳交不少費用),而Windows Phone已經沒落,所以實際上我只有用PhoneGap Build打包成Android APK而已。

我以前就有在「PhoneGap網頁樣板:簡單建立網站APP」寫過使用PhoneGap Build來建構網站APP的做法,跟Electron一樣,我也把它整合到混合應用程式框架中,成為大框架下的一份子。
值得一提的是PhoneGap Build雖然採用的是Apache Cordova核心,但這個名稱卻是描述在Adobe提供的伺服器上打包APP的整個流程,而不是像是Cordova在本機端部署環境、打包應用程式的做法。


功能 / Features

在混合應用程式框架中,我整理了一些在開發中可能會用到的功能,這是為了未來開發做的準備。這些功能包括了作為一個應用程式(APP)所需要的排版(包括主畫面、上方的工具列、左方的滑動選單)、主畫面的換頁、通知對話視窗、開啟外部連結、儲存由JSON產生的試算表檔案ODS、MVVM資料綁定、國際化語系、以遊戲引擎運作。以下簡單說明這些功能。

1. 排版與換頁 / Layout and Pages Navigation

image

混合應用程式使用了行動裝置為主的APP排版,主要畫面分成3大部分:1. 主畫面、2. 工具列 (toolbar)、3. 滑動選單 (sliding menu)。主畫面顯示該頁的主要內容,工具列會一直固定在畫面上方,而左邊的滑動選單則是會隨著你的畫面寬度動態的顯示、隱藏,你也可以按下工具列的選單按鈕來顯示滑動選單。

受益於OnsenUI的Auto Style功能,功能元件在不同平臺上會有些微的不同。舉例來說,下圖是iOS中的工具列:

image

下圖則是Android中的工具列:

image

我把換頁的方法包裝在onsenui_helper中,JavaScript語法如下:

onsenui_helper.switch_page(about)
2. 通知對話視窗 / Notification dialog

image

混合應用程式框架的使用了OnsenUI的$ons.notification功能來實作對話視窗。

受益於OnsenUI的Auto Style功能,上圖iOS風格的對話視窗在Android底下會變成下圖這個樣子:

image

通知功能的JavaScript語法如下:

vm.$ons.notification.confirm({      message: i18n.t("Are you sure to visit About?"),      callback: function (_result) {          console.log(_result);      }

});
3. 開啟外部連結 / Open link with your default browser

image

點下一些非APP內的連結時,混合應用程式框架提供了一些方法來讓他使用你預設的瀏覽器開啟。這部分我用hybird_app_helper來處理不同平臺的做法,JavaScript語法如下:

hybird_app_helper.open_window('https://github.com/pulipulichen/HTML5-Hybird-APP-Framework')
4. 儲存由JSON產生的試算表檔案ODS

image

這邊使用了JS-XLSX套件來讓JSON物件轉換成ODS試算表格式。JS-XLSX的功能複雜,網路上大部分的教學都亂七八糟,最好的用法還是官方自己提供的上傳分析轉換下載範例。我整理了JSON轉換成ODS Base64編碼的做法,寫在xlsx-helper.js檔案中。

另外的問題是如何讓裝置下載,或是說儲存、分享檔案。在網頁上,我使用的是FileSaver.js。在桌面端,我使用了Electron的ipcMain跟ipcRenderer來交換資料,讓網頁介面跟背後的Electron能夠彼此溝通,交給Electron寫入檔案。雖然Electron端已經很複雜了,但比不上在行動端的麻煩。在PhoneGap Build中,我先用cordova-plugin-file將ODS檔案寫入到cache暫存資料夾,然後使用PhoneGap / Cordova Social Sharing plugin來分享檔案。

5. MVVM資料綁定

image

混合應用程式框架使用了Vue.js的MVVM框架讓資料與顯示雙向綁定。主畫面中的「Hello, world!」綁定的是JavaScript中的「main_page.data.message」。顯示的程式設定如下:

image

而這個「main_page.data.message」則是寫在main_page.js中,資料的程式設定如下:

image

只要修改「main_page.data.message」的值,顯示畫面也會跟著改變。這是Vue.js這種MVVC框架的基本應用,相信熟悉MVVC框架的開發者應該很容易理解。

6. 國際化語系 / Localization

image

混合應用程式框架使用了javascript-i18n-core來作為語系檔的框架。以工具列中間的標題為例,我們可以用這個JavaScript語法來取得索引名稱「TITLE」的翻譯設定如下:

{{ i18n.t("TITLE") }}

語系檔放在www/locale資料夾中,裡面可以設定各個語系與對應索引名稱。

7. 以遊戲引擎運作:Cocoon.js / Game engine demo: Cocoon.js

image

為了挑戰混合應用程式框架的極限,我試著加入了Cocoon.js遊戲引擎框架所開發的兩款展示遊戲:SpaceSimDemoSuper Mario Bros

大家可以看看混合應用程式框架在網路瀏覽器、桌面端、行動裝置上玩遊戲是什麼感覺,也許未來也可以取代OnsenUI,使用Cocoon.js開發遊戲呢?


小結 / In closing

混合應用程式框架只是我為了未來可能會開發真正的跨平臺應用程式而做的一個小小的準備。我怕我現在不整理,以後可能就會忘記,所以做到這邊就趕快記錄一下。

30485967745854535578

(圖片來源:幫趣)

這整篇並沒有什麼提供給開發者的文件,請有心要使用這個框架的開發者自行trace code,程式碼都在GitHub上,請自行研讀、fork或下載吧。

開發感想 / Thoughts

接著來聊聊一些開發過程的想法。

混合應用程式框架實際上是要寫UI顯示畫面邏輯、 Electron跟桌面端底層作業系統的溝通、PhoneGap Build跟手機端底層裝置溝通的龐大架構。雖然都是用JavaScript來寫,但是不同框架下面使用的API都完全不一樣。雖然號稱跨平臺,但是還是要為不同平臺下很多功夫,實際上並沒有真的這麼跨平臺。

現在有很多開發框架是更加的跨平臺,像是Unity,但畢竟這個門檻比較高,不能直接將網頁使用的技術帶過去使用。而且對快速開發來說也不見得是比較方便。

用網頁來寫UI還是可以讓開發者有很大的自由度,特別是現在CSS3發展成熟,許多特別的UI介面又漂亮又好用,還能作出驚人的特效。我想未來我還是會繼續使用網頁來開發任何應用吧。

速度問題 / Performance issues

在開發的過程中很多人會問的是速度問題,這是PhoneGap令許多開發者詬病的問題。這個速度問題其實細分起來有兩個層面。第一個是載入,PhoneGap在開啟程式的時候,需要用「document.addEventListener("deviceready",function () {});」來確保cordova核心套件有正常載入,因此不管硬體效能多好,通常載入時間還是需要0.5秒。

image

一般來說我們會使用splash載入畫面讓使用者不會覺得太過突兀,在混合應用程式框架中也會預設使用icon.png作為載入背景畫面。

另一個問題是畫面運作時的流暢度。在Ionic Framework vs React Native Comparison這個影片中,我們可以看到基於Ionic Framework包裝的應用程式運作效能非常的差,已經到了妨礙使用體驗的程度了。但這個問題通常是因為程式寫的不好,最常見的情況,是因為開發者在轉場動畫上使用連續改變物件位置參數來營造動畫感,這卻是相當不流暢的做法。

現在比較好的做法是採用較好的顯示畫面框架,像是Onsen UI。或是根據How to Make High Performance PhoneGap Apps來修正你的程式架構。不過光是混合應用程式框架現在的架構,在各個平臺運作起來就已經很流暢了,我想就一般的應用程式來說是已經足夠使用。

如果要到玩遊戲的話,雖然看起來是可以運作Cocoon.js這種遊戲框架,但是還是不知道它的流暢度極限到底在那裡。但因為我可能比較不會朝遊戲的方向開發,這部分就待有心人士來測試吧。

其他跨平臺框架 / Cross platform framework

比起原生應用,我仍然比較偏好可以跨平臺的框架。

儘管許多知名的框架號稱自己是跨平臺,但其實也都是限制於行動裝置的平臺或桌面端的平臺,並不是真的能夠橫跨網頁、桌面端與行動裝置端,這也是我做這個混合應用程式框架的動機。
大部分的跨平臺框架有許多門檻,像是React NativeNativeScript。當然,這些門檻在混合應用框架裡面也是難以避免。

第一個門檻是編譯。幾乎現在主流的跨平臺框架都需要先進行編譯,然後才能展示最後的成果。只要框架使用了作業系統上的原生UI,幾乎都需要編譯後才能使用。React Native提供了Hot Reloading減少重複編譯的問題,但是還是需要部屬相關的編譯環境。相較之下,混合應用程式框架藉助了Adobe PhoneGab Build來編譯行動端的應用程式,我們只要用Git或壓縮成zip上傳即可,部屬環境不需要煩惱。但是如果要用Electron編譯成桌面端應用程式,那還是要用npm來安裝Electron需要的環境。Electron編譯環境所需要的指令請看README說明

第二個門檻是API。雖然框架提供了跨平臺都能使用的UI API,像是特定的按鈕、特定的工具列寫法,但這個框架的API常常已經有別於傳統網頁的做法,並非一般網頁開發者所謂的HTML5,幾乎等於你要重學一個語言。而這語言使用的API並不能帶到其他平臺上,像是網頁或桌面端的Electron,因此不能說是真的跨平臺。儘管混合應用框架本身也用了OnsenUI跟Vue.js等框架,讓人需要重新學習這些框架的用法。不過至少OnsenUI上所寫的工具列是真的可以在網頁、桌面端跟行動端正常顯示,而不會被原生UI的限制所綁架。

Electron的限制 / Huge file from Electron

相較於行動裝置端有許多大紅大紫的知名跨平臺框架,桌面端的跨平臺框架倒是只有Electron比較知名。混合應用程式框架也是使用Electron來實作桌面端應用程式的跨平臺開發。

然而,Electron最大的問題,還是它編譯出來的檔案大小實在是太大了。在www中的主要程式其實只有16MB左右,但混合應用程式框架最後編譯出來的檔案大小居然來到136MB,壓縮成7z也還有35MB之多。網路上有許多人對Electron檔案大小感到疑惑,Expected app bundle size?  中zcbenz說就是這樣大小,沒辦法。我也覺得很無奈。

剛好今天看到了新的桌面端跨平臺框架出現:Vuido,這是用Vue.js撰寫桌面端應用程式的框架,可以使用桌面端原生的UI元件,而不是像我使用混合應用框架中實際上是寫一個網頁來模擬UI介面的做法。檔案大小似乎比Electron還要小,速度上也應該會比Electron還要快吧。不過我個人還是比較喜歡寫網頁,用網頁做UI介面,因此應該不會選擇這個方案,還是繼續使用Electron吧。


好啦,混合應用程式框架就聊到這邊了。

你有想過要開發跨平臺的應用程式嗎?以前你都是怎麼做的呢?除了像混合應用程式框架使用的OnsenUI + Vue.js + Electron + PhoneGap Build之外,你還知道什麼不錯的框架嗎?對混合應用程式框架什麼問題的話,歡迎你在下面的留言發問。如果你也覺得混合應用程式框架是個開發跨平臺應用程式不錯的起點,請幫我在AddThis分享工具按讚、將這篇分享到Facebook等社群媒體吧!感謝你的耐心閱讀,讓我們下一篇見。

總共4 則留言 ( 我要發問 , 隱藏留言 顯示留言 )

  1. Hey, very nice site. I came across this on Google, and I am stoked that I did. I will definitely be coming back here more often. Wish I could add to the conversation and bring a bit more to the table, but am just taking in as much info as I can at the moment. Thanks for sharing.

    React Native Development Company

    回覆刪除
    回覆
    1. Thanks for your comments.

      React Native is one of popular hyper APP development solutions.
      However, the development environment is needed to compiler a React Native APP.
      In my opinion, it's not easy for the beginner who only know how to develop a Web APP.
      So this article I want to promote Web APP developers to build mobile APP and desktop APP with their prior knowledge.

      刪除
  2. 布丁學長你好,我是以前輔大動漫社的學弟,小綠。接下來我問的問題可能很蠢(因為我是一個完全沒有程式經驗的人XD),但還是請學長耐心聽我問完。我最近了解到HTML5好像可以開
    發跨平台式(手機、電腦終端等)的介面,所以我想深入了解這個內容,簡單說我想了解運用HTML5可以做到甚麼程度的設計(放圖?廣告?或是其他)?另外,就我看我學長這篇的內容後,我自己的了解是直接在後端的Server運用Onsen UI+Vue2開發ui介面,接著終端用Browser或是包裝成app的終端Browser連接到此,我這樣理解有錯嗎?如果沒錯的話,想請教下後端Server跟database又是怎麼連接?用甚麼程式語言呢?

    其實我有很多很笨的問題想請教,不知道學長有沒有空,方便的話請將聯絡方式mail給我(如我的發言身分),多謝

    回覆刪除
    回覆
    1. To 小綠,

      你把它想像成自帶瀏覽器的APP,可以跑HTML 5網頁,可以做HTML 5網頁能做到的事情,差不多就是這種感覺。

      從這個角度來回答你提問的細項:
      1. 「放圖?」可,你看網頁上有圖片,所以HTML 5就可以呈現圖片。
      2. 「廣告?」可,你看網頁上可以插入廣告,所以HTML 5就可以插入廣告,但是需要網路連線。
      3. 「後端的Server?」如果是用前端瀏覽器操作,你需要自己找伺服器架設。如果是打包成APP,則沒有後端Server,都是在單機上運作。
      4. 「終端用Browser?」如果是用網頁的方式運作,那就會依賴使用者使用的瀏覽器。如果是打包成APP,則不會用到終端的Browser,因爲APP就內建了運作HTML 5所需要的環境,不需要仰賴其他瀏覽器(例如Chrome、IE)。
      5. 「包裝成APP?」只有透過Electron或PhoneGap Build打包成APP的時候才是打包成APP。包裝工作是在開發的時候執行包裝,在使用的時候它就是一個APP。
      6. 「終端Browser連結到後端的Server?」在這裏不會用到終端Browser,也不會用到後端Server。
      7. 「後端Server怎麽連接?」基本運作都是本機端的單機運作。如果需要跟後端Server交換資料,請使用一般寫網頁時跟其他伺服器交換資料的方法,例如表單、iframe、AJAX。如果要突破CORS限制,請在Electron中請使用Node.js去發送request,在PhoneGap Build中請使用Cordova的API來傳送資料。
      8. 「跟database怎麽連接?」database種類很多,一般而言,HTML 5的資料庫是指儲存在單機上的資料庫,例如localStroage或Web SQL Database,用途是資料持久化,而非資料共享。如果是以server架設的database,例如PostgreSQL、MySQL、MSSQL,則請用跟後端Server連結的方式連結資料。如果是在Electron中,可以在Node.js中去遠端連接這些資料庫。
      9. 「用什麼程式語言呢?」HTML5 (HTML、JavaScript、CSS)、Electron / Node.js (JavaScript)、PhoneGap/Coadova (HTML、JavaScript、CSS)。
      10. 「不知道學長有沒有空?」偶爾我會在留言板回答問題,你提問我都會收到通知,有時間我就會回覆。
      11. 「請將聯絡方式mail給我。」這個做不到,因爲看不出你的mail是什麼。而且你的問題也會是大家的問題,放在這裏提問,大家以後就不用問相同的問題,可以造福社群。

      12. 「要怎麼追蹤後續問題?」
      好問題。
      https://1.bp.blogspot.com/--1_c72VHz0M/XIYYGbEiIHI/AAAAAAAEEXA/Yc74V9JUdCwpu4AshOepi8P31XJfGNQEwCLcBGAs/s1600/html5.png
      勾選留言框右下角的「通知我」即可。

      刪除