天天看點

require.js 入門學習 require.js 介紹 require.js意義 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:http://www.requirejs.org

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

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

require.js連結彙總:http://webeginner.diandian.com/post/2013-12-04/40060318517

繼續閱讀