山姆鍋對 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 總有點太過了 [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 來處理前端頁面導航以及其它功能 。

其它注意事項

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

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

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

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

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

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

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

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

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

    $.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 的應用專案 , 這是個不錯的選擇 。

參考資料


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