山姆鍋對 JavaScript 大多數框架都不算熟悉,但因為《我.影化身》專案需要提供 Web 介面, 不得已也要選擇符合自己需要的框架來運用一下。幸好,我的目標只是一個相對簡單的使用者介面, 讓這個過程縮短不少時間,最後決定使用 JQuery Mobile + Backbone 這樣的框架組合。
為什麼選擇使用 Web 用戶介面?
《我.影化身》專案有桌面環境用戶介面的需求,也嘗試過使用 Qt 這種跨平台的程式庫。 但山姆鍋對於產生的發行包因為這樣就要多將近 20MB 的大小一直覺得耿耿於懷,雖然這只是個人偏好問題, 還是希望有一個較輕量的跨平台用戶介面。
另外一個做法就是使用各個平台的特有程式庫來開發 UI,對於 Python 程式,這表示:
- 在 Windows 使用 PyWin32
- 在 OS X 使用 PyObjC
- 在 Linux 使用 PyGObject
理論上是可行,但要同時使用不同 API 在不同平台開發 UI, 只要 UI 稍微複雜點, 開發的困難度就會需要每個平台一個專職工程師了。《我.影化身》的確有使用上述 程式庫來整合桌面環境的訊息通知機制以及選單,但要開發完整 UI,即使像是《我.影化身》這種設定不需要複雜 UI 的程式,難度還是太高了。其它如 Toga, PyGUI 等等特殊的跨平台 UI 程式庫都因為缺乏專案所需的功能而無法採用。
不管原因為何,最後決定使用 Web 技術來提供跨平台 UI, 似乎也是很合理的方案。
為什麼選擇 JQuery Mobile + Backbone?
由於專業上需要,一直以來山姆鍋都有持續在關注 JavaScript 應用框架的發展, 也常常對於如雨後春筍般出現的新框架感到無所適從,所以之前都僅止於了解,並沒有太多實際運用的經驗。 如今要選擇專案所使用的框架,自然要先說說專案的需求:
提供一個類似應用程式(App)的用戶介面
: 因為這個專案就是個應用程式。
2.
不用太多 HTML5, CSS3 設計
: 作為工程師,山姆鍋對於設計的美感真的不在行。
3.
使用現有的用戶介面元件
: 希望開發的方式可以使用元件概念撰寫,也就是有明確的 Button, List,
Table 這樣的概念。
4.
同時支援桌面、平板以及手機裝置
: 沒有太多時間使用不同的框架。
應該也有其它方案符合需求,像是 Sencha Touch 等,但個人偏好 JQuery Mobile。 作為 UI 框架, JQuery Mobile 基本功能完整,但要開發應用總還是希望程式要有更好的結構, 這就需要考慮採用其它應用框架來輔助,目前跟 JQuery Mobile 搭配最好的看來是 Backbone。
雖然網路上有許多關於 JQuery Mobile + Backbone 的教學文章,JQuery Mobile 官網甚至有提供示範案例,但這些例子大多假設專案需求龐大,硬是要使用 RequireJS 來提供模組化開發機制。 對於《我.影化身》這樣的專案來說,使用 RequireJS 總有點太過了1 。 雖然網路上也找得到關於只使用 JQuery Mobile
- Backbone 的範例,但事情如果順利的話, 就不會有這篇文章了!
問題出在哪?
老實說,山姆鍋對 JavaScript 相關框架真的不熟,只是一邊開發一邊學習。 對於網路找到的 JQuery Mobile + Backbone 範例為何不能正常運作也沒有時間深入了解, 也許是單純版本問題,或者只是山姆鍋眼睛脫窗哪裏沒有設定好!不管怎樣,本來認為一件簡單的事情, 卡了一段時間後終於試出來一個可以運作的組合。
除了 JQuery,JQM 本身並不依賴其它程式庫,雖說是完整的框架,但因為它缺少其它 MV* 應用框架的功能, 讓它其實更像 Twitter Bootstrap 這種 UI 框架。為了開發完整的應用,通常還是搭配 Backbone 這樣的應用框架。
但因為 JQM 有自己的頁面路由(page
routing)的機制,所以需要設定讓它不處理這部分功能,而由 Backbone
全權處理。JQM 相關的設定在 /js/jqm-config.js
這個檔案:
// JQM config
$(document).on("mobileinit", function () {
$.mobile.ajaxEnabled = false;
$.mobile.linkBindingEnabled = false;
$.mobile.hashListeningEnabled = false;
$.mobile.pushStateEnabled = false;
$.mobile.defaultPageTransition = "fade";
$.mobile.autoInitializePage = false;
$("div[data-role='page']").on("pagehide", function (event, ui) {
$(event.currentTarget).remove();
});
});
\'mobileinit\' 事件
: 在 JQM 完成初始化前會呼叫此事件,所以在此設定 JQM。
-
ajaxEnabled, linkBindingEnabled, pushStateEnabled 都設為 false
: 分別讓 JQM 不使用 Ajax 方式動態載入頁面, 不處理超連結點選事件,
以及瀏覽器的導航歷史管理(navigation history),這些都會干擾
Backbone 的運作。
-
hashListeningEnabled 設為 false
: 告訴 JQM 不要處理瀏覽器 URL hash 更動事件。 URL
hash就是瀏覽器網址中 \'\#\' 那個部分,
這個部分按照規範是不會傳給後端伺服器,只能在網頁中存取。JQM 跟
Backbone 就是利用這個 hash 來決定要使用的頁面。
-
由於 JQM 不再負責頁面導航管理,需要由我們自己完成舊的頁面資料從 DOM 中移除的工作
: `pagehide` 事件處理便是用來完成這個工作。
有了這樣的設定,基本上就可以使用 Backbone 來處理前端頁面導航以及其它功能。
其它注意事項
有幾個在實際開發中遇到的問題:
動態產生 JQM 頁面內容時,可能會發現網頁呈現不太正常
: 這點在專案樣板中沒有實例。這是因為 JQM
沒有機會套用樣式到新增的網頁元素,需要特別通知 JQM:
``` javascript
$(this.el).trigger("create");
```
如果您有其它更好的方式,請不吝指教。
2.
新版本的更換頁面方式
: 新版本的 JQM 管理頁面的方式有更動,改為下列方式:
``` javascript
$(":mobile-pagecontainer").pagecontainer( "change", $(page.el), { changeHash: false});
```
網路上找到的文章大多使用舊的方法,也就是:
``` javascript
$.mobile.changePage($(page.el), {changeHash:false});
```
3.
jqm-config.js 的引入順序
: `/js/jqm-config.js` 是用來設定 JQM, 它必須在 JQuery
之後, JQM 之前在網頁中引入,如:
<script src="/js/jquery.min.js"></script>
<script src="/js/jqm-config.js"></script>
<script src="/js/jquery.mobile.min.js"></script>
JQM + Backbone 專案樣板
為了方便未來使用,山姆鍋在 GitHub 上整理了一個專案樣板:https://github.com/sampot/jqm-backbone-template ⎘
想要更實際的例子?那就請直接參考《我.影化身》的前端用戶介面實作(webfront):https://github.com/eavatar/eavatar-me ⎘ 。
結語
山姆鍋其實很喜歡 AngularJS 這個應用框架,但對於它的運作方式不是很了解,所以不在這個專案採用。 在實際使用後,發現 JQM + Backbone 這樣的組合還蠻不錯的:夠簡單、運作流程容易理解。 對於只需要簡單 Web UI 的應用專案,這是個不錯的選擇。
參考資料
- Using Backbone.js with jQuery Mobile : http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/ ⎘
Footnotes
-
如果預期專案需要多人一起開發或者功能較複雜,建議考慮使用 RequireJS。 ↩