基於RequireJS和JQuery的模塊化編程——常見問題解析

因爲js的代碼邏輯愈來愈重,一個js文件可能會有上千行,十分不利於開發與維護。最近正在把邏輯很重的js拆分紅模塊,在一頓糾結是使用requirejs仍是seajs的時候,最終仍是偏向於requirejs。畢竟官方文檔比較專業嘛...
不過即使是有完整的官方文檔,仍然遇到很多的問題,好比jquery-ui的使用。
下面就按部就班的講解一下我遇到的問題,以及解決的辦法。html

關於AMD和CMD的理解

AMD(異步模塊定義)的典型就是requirejs,而CMD(通用模塊定義)的典型是淘寶的seajs。jquery

他們的相同點是,都會異步的加載js。可是不一樣點是,require.js加載完會當即執行;而seajs則是等到進入主函數須要執行時才執行。編程

若是使用seajs初始的加載執行效率會比較高,可是在使用的過程當中可能會取執行js,所以可能會出現卡頓,影響用戶體驗(因爲我也沒試過,要是說錯了,別見怪)。而requirejs則是在一開始就把全部加載的js都執行,這時,若是你的模塊中有一些執行方法,它們可能並不會按照你想的順序執行。瀏覽器

所以,若是已經習慣了異步編程,而且但願有完善的文檔推薦使用requirejs;若是是想對執行順序有特殊要求,又方便開發,那麼也可使用seajs。app

如何解決requirejs中循環依賴問題

若是你定義的某個a模塊使用到了b模塊,而b模塊又使用了a模塊,那麼就會拋出循環依賴的異常。jquery插件

好比,我這裏寫了一個循環依賴的例子。
主頁面:異步

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script data-main="test.js" src="lib/require.js"></script>
</body>
</html>

主方法:異步編程

requirejs.config({
    baseUrl: './'
});

requirejs(['js/a'],function (a){
    console.log("in test");
    a.testfromb();
});

a.js模塊中,atest()方法提供b調用、testfromb()方法調用b的方法函數

define(function(require){
    var b = require("js/b");
    console.log("in a");
    return {
        atest:function(){
            console.log("test in a");
        },
        testfromb:function(){
            console.log("testfromb in a");
            b.btest();
        }
    }
});

b模塊中,調用了a的方法。requirejs

define(function(require){
    var a = require("js/a");
    console.log("in b");
    return {
        btest:function(){
            console.log("test in b");
            a.atest();
        }
    }
});

這樣至關於a調用了b的方法,可是b的方法依賴於a的方法,這就形成了循環依賴。瀏覽器會提示錯誤:

Uncaught Error: Module name "js/a" has not been loaded yet for context: _

按照官方文檔的說法,這種屬於設計的問題,應該儘可能避免。那麼若是避免不了該怎麼辦呢?能夠這樣修改b模塊:

define(function(require){
    // var a = require("js/a");
    console.log("in b");
    return {
        btest:function(){
            console.log("test in b");
            require("js/a").atest();
        }
    }
});

這裏是等到執行atest()方法時,才加載a模塊。這時,a模塊很顯然已經加載完了 。能夠看到輸出的信息:

in b
a.js:3 in a
test.js:6 in test
a.js:9 testfromb in a
b.js:6 test in b
a.js:6 test in a

一樣的方式,修改a可能就很差使了。這時由於模塊加載的順序是從b開始的。

關於循環依賴的源碼能夠參考雲盤

如何在requirejs中使用jquery

若是想要使用jquery比較簡單,直接在main.js中添加對應的依賴便可:

requirejs.config({
    baseUrl: './',
    paths:{
        'jquery':'lib/jquery'
    }
});

requirejs(['jquery'],
function ($){
    $('#test').html('test');
});

如何在requirejs中使用jquery插件

對於jquery的插件,比較常見的作法都是傳入一個jquery的對象,在這個jquery對象的基礎上添加插件對應的方法。

首先須要添加jquery插件的依賴,這裏用兩個插件舉例子——jquery-ui和jquery-datatables

requirejs.config({
    baseUrl: './',
    paths:{
        'jquery':'lib/jquery',
        'jquery-ui':'lib/jquery-ui',
        'jquery-dataTables':'lib/jquery.dataTables'
    },
    shim:{
        'jquery-ui':['jquery'],
        'jquery-dataTables':['jquery']
    }
});

requirejs(['jquery','jquery-ui','jquery-dataTables'],
function ($){
    ....
});

因爲jquery插件都須要依賴於jquery,所以能夠在shim中指定依賴關係。
除了上面這種使用方法,也可使用commonJS風格的調用:

define(function(require){
    var $ = require('jquery');
    require('jquery-ui');
    require('jquery-dataTables');
    
        //下面都是測試,能夠忽略
    var _test = $('#test');
    _test.selectmenu({
        width : 180,
        change : function(event, ui) {
            console.log('change');
        }
    });
    return {
        test:function(){
            //測試jquery-ui
            _test.append($('<option>test1</option><option>test1</option>'));
            _test.selectmenu("refresh");
            //測試jquery-datatables
            var _table = $('table');
            _table.dataTable();
        }
    }
});

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

Uncaught TypeError: _table.dataTable is not a function

這是由於,dataTables並非一個require風格的模塊,所以直接這樣引入,並不會執行它內部的匿名函數。能夠修改它的匿名函數,傳入$對象,在最後一行:

*/

    return $.fn.dataTable;
//}));原來是這樣
}($)));//這裏增長執行這個匿名函數,而且傳入$對象。

}(window, document));

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

樣例代碼能夠參考雲盤,因爲引入的資源不是很全,因此會報錯,能夠直接忽略,由於能執行UI插件就表示已經成功了。

requirejs使用jquery-ui的問題

因爲requirejs加載js文件後會當即執行,若是你的jquery ui 插件須要刷新DOM頁面,那麼可能會致使頁面的事件失效。

好比,你的模塊在加載後,對頁面的某個元素$('#test')綁定了click事件。可是使用了某個UI插件,這個插件會從新渲染DOM元素,test對應的click事件就失效了。

解決辦法:

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

好比在DOM重構的JS模塊中,執行渲染的代碼下面:

require("xxx").initEvents();

常見場景:

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

相關文章
相關標籤/搜索