JavaScript 模塊化編程

字號(hào):


    一直對(duì)JS都是一知半解,最近遇到這方面問(wèn)題,所以在網(wǎng)上學(xué)習(xí)了一下,現(xiàn)在還沒(méi)有完全明白,先貼出筆記;
    第一章 JavaScript模塊化編程
    (一):模塊的寫(xiě)法
    一 原始寫(xiě)法
    // 模塊就是實(shí)現(xiàn)特定功能的一組方法;只要把不同的函數(shù)(以及記錄狀態(tài)的變量)簡(jiǎn)單地放在一起,就算是一個(gè)模塊;
    function m1(){
    // ...
    }
    function m2(){
    // ...
    }
    // 上面的函數(shù)m1()和m2(),組成一個(gè)模塊;使用時(shí)直接調(diào)用就行;
    // 缺點(diǎn):"污染"了全局變量; 無(wú)法保證不與其他模塊發(fā)生變量名沖突,而且模塊成員之間看不出直接關(guān)系;
    二 對(duì)象寫(xiě)法
    // 把模塊寫(xiě)成一個(gè)對(duì)象,所有的模塊成員都放到這個(gè)對(duì)象里面;
    var module = new Object({
    _count:0,
    m1:function(){
    // ...
    },
    m2:function(){
    // ...
    }
    });
    // 上面的函數(shù)m1()和m2(),都封裝在module對(duì)象里;使用時(shí)直接調(diào)用這個(gè)對(duì)象的屬性;
    module.m1();
    // 但是,這樣的寫(xiě)法會(huì)暴露所有模塊成員,內(nèi)部狀態(tài)可以被外部改寫(xiě);
    module._count = 4;
    三 立即執(zhí)行函數(shù)寫(xiě)法
    var module = (function(){
    var _count = 0;
    var m1 = function(){
    // ...
    };
    var m2 = function(){
    };
    return {
    m1:m1,
    m2:m2
    };
    })();
    // 使用上面的寫(xiě)法,外部代碼無(wú)法讀取內(nèi)部的_count變量;
    console.info(module._count); // undefined;
    // 上面的寫(xiě)法就是JavaScript模塊的基本寫(xiě)法;
    四 放大模式
    1
    2
    3
    4
    5
    6
    7
    8
    // 如果模塊很大,必須分成幾個(gè)部分,或者一個(gè)模塊需要繼承另一個(gè)模塊,這時(shí)就有必要采用"放大模式";
    var module = (function(mod){
    mod.m3 = function(){
    // ...
    };
    return mod;
    })(module);
    // 上面的代碼為module模塊添加了一個(gè)新方法m3(),然后返回新的module模塊;
    五 寬放大模式
    1
    2
    3
    4
    5
    6
    7
    // 在瀏覽器環(huán)境中,模塊的各個(gè)部分通常都是從網(wǎng)上獲取的,有時(shí)無(wú)法知道哪個(gè)部分會(huì)先加載;
    // 如果采用上一節(jié)的寫(xiě)法,第一個(gè)執(zhí)行的部分有可能加載一個(gè)不存在的空對(duì)象,這時(shí)就要采用"寬放大模式";
    var module = (function(mod){
    // ...
    return mod;
    })(window.module || {});
    // 與"放大模式"相比,"寬放大模式"就是"立即執(zhí)行函數(shù)"的參數(shù)可以是空對(duì)象;
    六 輸入全局變量
    1
    2
    3
    4
    5
    6
    7
    // 獨(dú)立性是模塊的重要特點(diǎn),模塊內(nèi)部最好不與程序的其他部分直接交互;
    // 為了在模塊內(nèi)部調(diào)用全局變量,必須顯式地將其他變量輸入模塊;
    var module = (function($,YAHOO){
    // ...
    })(jQuery,YAHOO);
    // 上面的module模塊需要使用jQuery庫(kù)和YUI庫(kù),就把這兩個(gè)庫(kù)(其實(shí)是兩個(gè)模塊)當(dāng)作參數(shù)輸入module;
    // 這樣做除了保證模塊的獨(dú)立性,還使得模塊之間的依賴(lài)關(guān)系變得明顯;
    第二章 JavaScript模塊化編程(二):AMD規(guī)范
    一 模塊的規(guī)范
    // 目前,通行的JavaScript模塊規(guī)范共有兩種:CommonJS和AMD;
    二 CommonJS
    // node.js將javascript語(yǔ)言用于服務(wù)器端編程,這標(biāo)志"JavaScript模塊化編程"正式誕生;
    // node.js的模塊系統(tǒng),就是參照CommonJS規(guī)范實(shí)現(xiàn)的;
    在CommonJS中,有一個(gè)全局性方法require(),用于加載模塊;
    var math = require('math'); // 加載模塊;
    math.add(2,3); // 調(diào)用模塊方法=>5;
    三 瀏覽器環(huán)境
    // 上一節(jié)的代碼在瀏覽器中運(yùn)行會(huì)有很大的問(wèn)題;
    var math = require('math');
    math.add(2,3);
    // 問(wèn)題:必須在require('math')等math.js加載完成,才會(huì)執(zhí)行math.add(2,3);
    // 所以瀏覽器的模塊,不能采用"同步加載",只能采用"異步加載";==>AMD;
    四 AMD
    AMD(Asynchronous Module Definition)異步模塊定義;
    // 采用異步加載模塊,模塊的加載不影響它后面語(yǔ)句的運(yùn)行,所有依賴(lài)這個(gè)模塊的語(yǔ)句,都定義在一個(gè)回調(diào)函數(shù)中,
    // 等加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行;
    // AMD也采用require()語(yǔ)句加載模塊,但是它要求兩個(gè)參數(shù):
    require([module],callback);
    // module:是一個(gè)數(shù)組,里面的成員就是要加載的模塊;
    // callback:是加載成功之后的回調(diào)函數(shù);
    require(['math'],function(math){
    math.add(2,3);
    });
    // math.add()與math模塊加載不是同步的,瀏覽器不會(huì)發(fā)生假死;所以,AMD比較適合瀏覽器環(huán)境;
    第三章 JavaScript模塊化編程(三):require.js的用法
    一 為什么使用require.js
    // 需要依次加載多個(gè)js文件;
    // 缺點(diǎn):
    // 1.加載的時(shí)候,瀏覽器會(huì)停止網(wǎng)頁(yè)渲染,加載文件越多,網(wǎng)頁(yè)失去響應(yīng)的時(shí)間就會(huì)越長(zhǎng);
    // 2.由于js文件之間存在依賴(lài)關(guān)系,因此必須嚴(yán)格保證加載順序,當(dāng)依賴(lài)關(guān)系很復(fù)雜的時(shí)候,代碼的編寫(xiě)和維護(hù)都會(huì)變得困難;
    // 所以require.js解決了這兩個(gè)問(wèn)題:
    // 1.實(shí)現(xiàn)js文件的異步加載,避免網(wǎng)頁(yè)失去響應(yīng);
    // 2.管理模塊之間的依賴(lài)性,便于代碼的編寫(xiě)和維護(hù);
    二 require.js的加載
    1.加載require.js
    <script scr="js/require.js" defer async="true"></script>
    // async屬性表明這個(gè)文件需要異步加載,避免網(wǎng)頁(yè)失去響應(yīng);IE不支持這個(gè)屬性,只支持defer,所以把defer也寫(xiě)上;
    2.加載main.js
    <script src="js/require.js" data-main="js/main"></script>
    // data-main屬性的作用是,指定網(wǎng)頁(yè)程序的主模塊=>main.js,這個(gè)文件會(huì)第一個(gè)被require.js加載;
    // 由于require.js默認(rèn)的文件后綴名是js,所以可以把main.js簡(jiǎn)寫(xiě)成main;
    三 主模塊main.js的寫(xiě)法
    1.如果main.js不依賴(lài)任何其他模塊,可以直接寫(xiě)入JavaScript代碼;
    // main.js
    alert('加載成功!');
    2.如果main.js依賴(lài)于模塊,這時(shí)就要使用AMD規(guī)范定義的require()函數(shù);
    // main.js
    require(['moduleA','moduleB','moduleC'],function(moduleA,moduleB,moduleC){
    // ...
    })
    // require()函數(shù)接收兩個(gè)參數(shù):
    // 參數(shù)一:數(shù)組,表示所依賴(lài)的模塊,即主模塊依賴(lài)的三個(gè)模塊;
    // 參數(shù)二:回調(diào)函數(shù),當(dāng)前面指定的模塊都加載成功后,它將被調(diào)用;加載的模塊會(huì)以參數(shù)形式傳入該函數(shù),從而在回調(diào)函數(shù)內(nèi)部可以使用這些模塊;
    // require()異步加載模塊,瀏覽器不會(huì)失去響應(yīng);它指定的回調(diào)函數(shù),只有前面的模塊都加載成功后,才會(huì)運(yùn)行,解決了依賴(lài)性的問(wèn)題;
    實(shí)例:
    require(['jquery','underscore','backbone'],function($,_,Backbone){
    // ...
    });
    四 模塊的加載
    // 使用require.config()方法,可以對(duì)模塊的加載行為進(jìn)行自定義;
    // require.config()就寫(xiě)在主模塊(main.js)的頭部;
    // 參數(shù)就是一個(gè)對(duì)象,這個(gè)對(duì)象的paths屬性指定各個(gè)模塊的加載路徑;
    // 設(shè)定以下三個(gè)模塊的文件默認(rèn)和main.js在用一個(gè)目錄;
    require.config({
    paths:{
    "jquery":"jquery.min",
    "underscore":"underscore.min",
    "backbone":"backbone.min"
    }
    });
    // 如果加載的模塊和主模塊不在同一個(gè)目錄,就要逐一指定路徑;
    require.config({
    paths:{
    "jquery":"lib/jquery.min",
    "underscore":"lib/underscore.min",
    "backbone":"lib/backbone.min"
    }
    });
    // 或者直接改變基目錄(baseUrl)
    require.config({
    baseUrl:"js/lib",
    paths:{
    "jquery":"jquery.min",
    "underscore":"underscore.min",
    "backbone":"backbone.min"
    }
    });
    // 如果模塊在另一臺(tái)主機(jī)上,也可以直接指定它的網(wǎng)址
    require.config({
    paths:{
    "jquery":""
    }
    });
    // require.js要求,每個(gè)模塊是一個(gè)單獨(dú)的js文件;這樣的話(huà),如果加載多個(gè)模塊,就會(huì)發(fā)出多次HTTP請(qǐng)求,會(huì)影響網(wǎng)頁(yè)的加載速度;
    // 因此,require.js提供了一個(gè)優(yōu)化工具,當(dāng)模塊部署完畢以后,可以用這個(gè)工具將多個(gè)模塊合并在一個(gè)文件中,減少HTTP請(qǐng)求數(shù);
    五 AMD模塊的寫(xiě)法
    // require.js加載的模塊,采用AMD規(guī)范,也就是說(shuō),模塊必須按照AMD的規(guī)定來(lái)寫(xiě);
    // 具體來(lái)說(shuō),就是模塊必須采用特定的define()函數(shù)來(lái)定義;如果一個(gè)模塊不依賴(lài)其他模塊,那么可以直接定義在define()函數(shù)中;
    // 在math.js中定義math模塊
    // math.js
    define(function(){
    var add = function(x,y){
    return x+y;
    };
    return {
    add:add
    };
    });
    // 在main.js中加載math模塊
    require(['math'],function(math){
    alert(math.add(1,1));
    });
    // 如果這個(gè)模塊還依賴(lài)其他模塊,那么define()函數(shù)的第一個(gè)參數(shù),必須是一個(gè)數(shù)組,指明該模塊的依賴(lài)性;
    // math.js
    define(['myLib'],function(myLib){
    function foo(){
    myLib.doSomething();
    }
    return {
    foo:foo
    };
    });
    // 當(dāng)require()函數(shù)加載上面這個(gè)模塊的時(shí)候,就會(huì)先加載myLib.js文件;
    六 加載非規(guī)范的模塊
    // 加載非規(guī)范的模塊,在用require()加載之前,要先用require.config()方法,定義它們的一些特征;
    require.config({
    shim:{
    'underscore':{
    exports:'_'
    },
    'backbone':{
    deps:['underscore','jquery'],
    exports:'Backbone'
    }
    }
    });
    // require.config()接收一個(gè)配置對(duì)象,這個(gè)對(duì)象除了有前面說(shuō)過(guò)的paths屬性之外,還有一個(gè)shim屬性,專(zhuān)門(mén)用來(lái)配置不兼容的模塊;
    // (1).定義deps數(shù)組,表明該模塊的依賴(lài)性;
    // (2).定義exports值(輸出的變量名),表明這個(gè)模塊外部調(diào)用時(shí)的名稱(chēng);
    比如:jQuery的插件
    shim:{
    'jquery.scroll':{
    deps:['jquery'],
    exports:'jQuery.fn.scroll'
    }
    };
    七 require.js插件
    1.domready:可以讓回調(diào)函數(shù)在頁(yè)面DOM結(jié)構(gòu)加載完成之后運(yùn)行;
    require(['domready!'],function(doc){
    // called once the DOM is ready;
    })
    2.text和image:允許require.js加載文本和圖片文件;
    define(['text!review.txt','image!cat.jpg'],function(review,cat){
    console.log(review);
    document.body.appendChild(cat);
    });