require.js 入門學習

require.js 介紹

//————————————————————-

AMD規范

前端開發在近一兩年發展的非常快,JavaScript作為主流的開發語言得到瞭前所未有的熱捧。大量的前端框架出現瞭,這些框架都在嘗試著解決一些前端開發中的共性問題,但是實現又不盡相同。在這個背景下,CommonJS社區誕生瞭,為瞭讓前端框架發展的更加成熟,CommonJS鼓勵開發人員一起在社區裡為一些完成特定功能的框架制定規范。AMD(Asynchronous Module Definition)就是其中的一個規范。

傳統JavaScript代碼的問題

讓我們來看看一般情況下JavaScript代碼是如何開發的:通過<script>標簽來載入JavaScript文件,用全局變量來區分不同的功能代碼,全局變量之間的依賴關系需要顯式的通過指定其加載順序來解決,發佈應用時要通過工具來壓縮所有的JavaScript代碼到一個文件。當Web項目變得非常龐大,前端模塊非常多的時候,手動管理這些全局變量間的依賴關系就變得很困難,這種做法顯得非常的低效。

AMD的引入

AMD提出瞭一種基於模塊的異步加載JavaScript代碼的機制,它推薦開發人員將JavaScript代碼封裝進一個個模塊,對全局對象的依賴變成瞭對其他模塊的依賴,無須再聲明一大堆的全局變量。通過延遲和按需加載來解決各個模塊的依賴關系。模塊化的JavaScript代碼好處很明顯,各個功能組件的松耦合性可以極大的提升代碼的復用性、可維護性。這種非阻塞式的並發式快速加載JavaScript代碼,使Web頁面上其他不依賴JavaScript代碼的UI元素,如圖片、CSS以及其他DOM節點得以先加載完畢,Web頁面加載速度更快,用戶也得到更好的體驗。

CommonJS的AMD規范中隻定義瞭一個全局的方法,如清單1所示。
清單1.AMD規范

define(id?, dependencies?, factory);  

該方法用來定義一個JavaScript模塊,開發人員可以用這個方法來將部分功能模塊封裝在這個define方法體內。

id表示該模塊的標識,為可選參數。
dependencies是一個字符串Array,表示該模塊依賴的其他所有模塊標識,模塊依賴必須在真正執行具體的factory方法前解決,這些依賴對象加載執行以後的返回值,可以以默認的順序作為factory方法的參數。dependencies也是可選參數,當用戶不提供該參數時,實現AMD的框架應提供默認值為[“require”,”exports”,“module”]。
factory是一個用於執行改模塊的方法,它可以使用前面dependencies裡聲明的其他依賴模塊的返回值作為參數,若該方法有返回值,當該模塊被其他模塊依賴時,返回值就是該模塊的輸出。
CommonJS在規范中並沒有詳細規定其他的方法,一些主要的AMD框架如RequireJS、curl、bdload等都實現瞭define方法,同時各個框架都有自己的補充使得其API更實用。

require.js意義

//————————————————————-

模塊化

其實在計算機領域,模塊化的概念被推崇瞭近四十年。軟件總體結構體現模塊化思想,即把軟件劃分為一些獨立命名的部件,每個部件稱為一個模塊,當把所有模塊組裝在一起的時候,便可獲得問題的一個解。
模塊化以分治法為依據,但是否意味著我們把軟件無限制的細分下去。事實上當分割過細,模塊總數增多,每個模塊的成本確實減少瞭,但模塊接口所需代價隨之增加。要確保模塊的合理分割則須瞭解信息隱藏,內聚度及耦合度。
這裡的JavaScript模塊與傳統的JavaScript代碼不一樣的地方在於它無須訪問全局的變量。模塊化的設計使得JavaScript代碼在需要訪問”全局變量”的時候,都可以通過依賴關系,把這些”全局變量”作為參數傳遞到模塊的實現體裡,在實現中就避免瞭訪問或者聲明全局的變量或者函數,有效的避免大量而且復雜的命名空間管理。
如同CommonJS的AMD規范所述,定義JavaScript模塊是通過define方法來實現的。

命名空間

Javascript因為天生的缺點,語言本身沒有集成命名空間的概念,所以變量名、函數名很容易發生沖突。這些年,大傢想盡瞭各種辦法,給 js 添加命名空間的概念,其中最成熟的套路,就是 RequireJS 這種。

如果你對 c# 或 java 語言的命名空間有一點點瞭解,那麼,RequireJS 的用法和作用,可以用 c# 中的兩行代碼來類比說明:

using xx=wojilu.Core;
namespace MyApp {
    public class MyClass {
        public void MyTest() {}
    }
}

require.js主要的功能,就是實現c#代碼的第一行和第二行的功能:1)引入需要使用的命名空間(順便加個別名也行);2)將自己的代碼放到命名空間中,避免全局污染。

下面我們看一下 RequireJS 的具體寫法,新建一個獨立的 wojilu.test1.js 文件,然後輸入如下代碼:

define( ['wojilu.Core'], function(xx) {
    return {
        MyTest : function() {alert( 'wojilu1 module' );}
    };
});

和上面的c#代碼對比一下,require.js 同樣也做瞭兩件事情:
1、引入命名空間 wojilu.Core,同時給它取瞭一個別名 xx;
2、將自己的所有代碼放在 define 中,避免全局化的污染沖突。
總之,require.js定義瞭(define)一個命名空間,在定義的時候,順便引用瞭需要使用其他命名空間。我們註意到,按照require.js 的術語,它把命名空間叫做“模塊”。註意,在這裡,require.js 定義的模塊(命名空間)是匿名的,沒有取名,這是和c#不同的地方。

require.js實戰

//————————————————————-

引入require.js

<script src="require.js"></script>

有人可能會想到,加載這個文件,也可能造成網頁失去響應。解決辦法有兩個,一個是把它放在網頁底部加載,另一個是寫成下面這樣:

<script src="js/require.js" defer async="true" ></script>

async屬性表明這個文件需要異步加載,避免網頁失去響應。IE不支持這個屬性,隻支持defer,所以把defer也寫上。

這種寫法雖然簡單,但其實並不推薦,一般的寫法還要再加個屬性:

<script data-main="js/main" src="js/require-jquery.js"></script>

就像一個c程序總有一個 main 方法作為入口一樣,上面這種寫法,做瞭幾件事情:
1、加載瞭 require-jquery.js 文件。註意,官方提供瞭 require.js和 jquery 的打包版本,推薦。
2、在加載之後,加載入口文件 js/main.js ,註意,main.js 寫進去的時候,不需要後綴名。
你的所有其他 js 模塊文件,都可以寫在 main.js 裡面,通過 main.js 加載。

require.js用法(一):define定義模塊

define( ['wojilu.test1'], function( t1 ) {
    return {
        NewTest : function() { t1.MyTest();}
    };
});

通過文件名(不需要後綴名),引入瞭 wojilu.test1 命名空間,並給它取瞭別名 t1,然後在代碼中使用 t1.MyTest() 方法。

按照 RequireJS 的規范,所有的模塊定義,都必須放在 return {} 對象中。也就是說,你的代碼都要寫在 return 返回的 {} 對象裡面。這會不會導致代碼臃腫難看?當然不會。你可以重構一下,比如這樣做:

define代碼

define(['wojilu.test1'], function (t1) {
    function someFunc1() {
        // 實際主要代碼
    }
    function someFunc2() {
        // 實際主要代碼
    }
    // 通過 return 方式,將需要公開的函數暴露出來,供其他 js 調用
    return {
        NewTest: function () {
            t1.MyTest();
        },
        fun1: someFunc1,
        fun2: someFunc2
    };
});

require.js用法(二):require加載js文件

到此為止,我們遇到瞭兩個關鍵詞,一個是 define ,可以用來定義模塊(命名空間),第一部分我們講瞭;還有一個是 require,可以直接加載其他 js。它除瞭簡單的用法:

<script>
require( ["some" ] );
</script>

之外,還有和 define 類似的復雜用法:

<script> 
require(["aModule", "bModule"], function() { 
    myFunctionA(); // 使用 aModule.js 中的函數 myFunctionA
    myFunctionB(); // 使用 bModule.js 中的函數 myFunctionB
}); 
</script> 

總結一下,define 是你定義自己的模塊的時候使用,可以順便加載其他js;require 直截瞭當,供你加載用的,它就是一個加載方法,加載的時候,可以定義別名。

require.js用法(三):require.config()對模塊加載行為自定義

require.config()就寫在主模塊(main.js)的頭部。參數就是一個對象,這個對象的paths屬性指定各個模塊的加載路徑。

 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"
    }
  });

如果某個模塊在另一臺主機上,也可以直接指定它的網址,比如:

require.config({
    paths: {
      "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
    }
});

require.js用法(四):define模塊返回函數個數問題

1、define 的return隻有一個函數,require的回調函數可以直接用別名代替該函數名。

2、define 的return當有多個函數,require的回調函數必須用別名調用所有的函數。

require(['selector','book'], function(query,book) {
    var els = query('.wrapper');

    book.fun1();
    book.fun2();
});

此處query 函數是1的情況,book 函數是2的情況。

require.js用法(五):加載的js文件不是AMD規范定義

當調用的js文件不是使用define定義,直接使用其全局變量或函數就可以瞭,沒有影響。

這個時候隻是控制瞭js 文件的加載順序。

@@部分資料來自網絡,感謝偉大的樂於分享的程序猿們!

require.js:https://www.requirejs.org

require.js入門教程:https://www.verydemo.com/demo_c110_i2031.html

使用RequireJS優化Web應用前端:https://www.csdn.net/article/2012-09-27/2810404

You May Also Like