山姆鍋對 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 應用框架的發展, 也常常對於如雨後春筍般出現的新框架感到無所適從,所以之前都僅止於了解,並沒有太多實際運用的經驗。 如今要選擇專案所使用的框架,自然要先說說專案的需求:

  1. 提供一個類似應用程式 (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 總有點太過了 < sup class="footnote-ref">[1] 。 雖然網路上也找得到關於只使用 JQuery Mobile

  • Backbone 的範例,但事情如果順利的話, 就不會有這篇文章了!

問題出在哪?

老實說,山姆鍋對 JavaScript 相關框架真的不熟,只是一邊開發一邊學習。 對於網路找到的 JQuery Mobile + Backbone 範例為何不能正常運作也沒有時間深入了解, 也許是單純版本問題,或者只是山姆鍋眼睛脫窗哪裏沒有設定好!不管怎樣,本來認為一件簡單的事情, 卡了一段時間後終於試出來一個可以運作的組合。

除了 JQuery,JQM 本身並不依賴其它程式庫,雖說是完整的框架,但因為它缺少其它 MV* 應用框架的功能, 讓它其實更像 Twitter Bootstrap 這種 UI 框架。為了開發完整的應用,通常還是搭配 Backbone 這樣的應用框架。

但因為 JQM 有自己的頁面路由(page routing)的機制,所以需要設定讓它不處理這部分功能,而由 Backbone 全權處理。JQM 相關的設定在 /js/jqm-config.js 這個檔案:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 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 來處理前端頁面導航以及其它功能。

其它注意事項

有幾個在實際開發中遇到的問題:

  1. 動態產生 JQM 頁面內容時,可能會發現網頁呈現不太正常

    這點在專案樣板中沒有實例。這是因為 JQM 沒有機會套用樣式到新增的網頁元素,需要特別通知 JQM:

    1
    $(this.el).trigger("create");

    如果您有其它更好的方式,請不吝指教。

  2. 新版本的更換頁面方式

    新版本的 JQM 管理頁面的方式有更動,改為下列方式:

    1
    $(":mobile-pagecontainer").pagecontainer( "change", $(page.el), { changeHash: false});

    網路上找到的文章大多使用舊的方法,也就是:

    1
    $.mobile.changePage($(page.el), {changeHash:false});
  3. jqm-config.js 的引入順序
    /js/jqm-config.js 是用來設定 JQM, 它必須在 JQuery 之後, JQM 之前在網頁中引入,如:
1
2
3
<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 的應用專案,這是個不錯的選擇。

參考資料


如果預期專案需要多人一起開發或者功能較複雜,建議考慮使用 RequireJS。