基於RequireJS和JQuery的模組化程式設計——常見問題解析

發表於2016-04-15

由於js的程式碼邏輯越來越重,一個js檔案可能會有上千行,十分不利於開發與維護。最近正在把邏輯很重的js拆分成模組,在一頓糾結是使用requirejs還是seajs的時候,最終還是偏向於requirejs。畢竟官方文件比較專業嘛…
不過即便是有完整的官方文件,仍然遇到不少的問題,比如jquery-ui的使用。
下面就循序漸進的講解一下我遇到的問題,以及解決的辦法。

關於AMD和CMD的理解

AMD(非同步模組定義)的典型就是requirejs,而CMD(通用模組定義)的典型是淘寶的seajs。

他們的相同點是,都會非同步的載入js。但是不同點是,require.js載入完會立即執行;而seajs則是等到進入主函式需要執行時才執行。

如果使用seajs初始的載入執行效率會比較高,但是在使用的過程中可能會取執行js,因此可能會出現卡頓,影響使用者體驗(由於我也沒試過,要是說錯了,別見怪)。而requirejs則是在一開始就把所有載入的js都執行,這時,如果你的模組中有一些執行方法,它們可能並不會按照你想的順序執行。

因此,如果已經習慣了非同步程式設計,並且希望有完善的文件推薦使用requirejs;如果是想對執行順序有特殊要求,又方便開發,那麼也可以使用seajs。

如何解決requirejs中迴圈依賴問題

如果你定義的某個a模組使用到了b模組,而b模組又使用了a模組,那麼就會丟擲迴圈依賴的異常。

比如,我這裡寫了一個迴圈依賴的例子。
主頁面:

主方法:

a.js模組中,atest()方法提供b呼叫、testfromb()方法呼叫b的方法

b模組中,呼叫了a的方法。

這樣相當於a呼叫了b的方法,但是b的方法依賴於a的方法,這就造成了迴圈依賴。瀏覽器會提示錯誤:

按照官方文件的說法,這種屬於設計的問題,應該儘量避免。那麼如果避免不了該怎麼辦呢?可以這樣修改b模組:

這裡是等到執行atest()方法時,才載入a模組。這時,a模組很顯然已經載入完了 。可以看到輸出的資訊:

同樣的方式,修改a可能就不好使了。這時因為模組載入的順序是從b開始的。

關於迴圈依賴的原始碼可以參考雲盤

如何在requirejs中使用jquery

如果想要使用jquery比較簡單,直接在main.js中新增對應的依賴即可:

如何在requirejs中使用jquery外掛

對於jquery的外掛,比較常見的做法都是傳入一個jquery的物件,在這個jquery物件的基礎上新增外掛對應的方法。

首先需要新增jquery外掛的依賴,這裡用兩個外掛舉例子——jquery-ui和jquery-datatables

由於jquery外掛都需要依賴於jquery,因此可以在shim中指定依賴關係。
除了上面這種使用方法,也可以使用commonJS風格的呼叫:

不過,執行上面的程式碼,會報一個異常:

這是因為,dataTables並不是一個require風格的模組,因此直接這樣引入,並不會執行它內部的匿名函式。可以修改它的匿名函式,傳入$物件,在最後一行:

這也是在網上搜的方法,原理奈何經驗不足….

樣例程式碼可以參考雲盤,由於引入的資源不是很全,所以會報錯,可以直接忽略,因為能執行UI外掛就表示已經成功了。

requirejs使用jquery-ui的問題

由於requirejs載入js檔案後會立即執行,如果你的jquery ui 外掛需要重新整理DOM頁面,那麼可能會導致頁面的事件失效。

比如,你的模組在載入後,對頁面的某個元素$(‘#test’)繫結了click事件。但是使用了某個UI外掛,這個外掛會重新渲染DOM元素,test對應的click事件就失效了。

解決辦法:

  • 把事件繫結推遲到DOM元素渲染完後再手動觸發繫結;
  • 也可以使用事件捕獲代替DOM元素的事件繫結(太麻煩了…不推薦)。

比如在DOM重構的JS模組中,執行渲染的程式碼下面:

常見場景:

比如我在頁面中使用了jquery-steps這個UI外掛,它會對頁面進行重新渲染。這就導致我最開始繫結的事件都失效了….只有推遲到這個js重構完頁面,再繫結才行。

相關文章