RequireJS & SeaJS

在 模塊化開(kāi)發(fā) 開(kāi)發(fā)以前,都是直接在頁(yè)面上引入 script 標(biāo)簽來(lái)引用腳本的,當(dāng)項(xiàng)目變得比較復(fù)雜,就會(huì)帶來(lái)很多問(wèn)題。

  • JS項(xiàng)目中的依賴(lài)只有通過(guò)引入JS的順序來(lái)確定,項(xiàng)目會(huì)變得復(fù)雜難以維護(hù)。

  • 復(fù)雜的腳本會(huì)暴露很多全局變量, 比如 $,_. ... 。

  • 同步加載的時(shí)候,網(wǎng)頁(yè)會(huì)停止渲染,加載時(shí)間越長(zhǎng),網(wǎng)頁(yè)失去響應(yīng)的事件就越長(zhǎng)。

于是,AMD 規(guī)范就誕生了,AMD 即為異步模塊定義,有效避免同步加載導(dǎo)致頁(yè)面的假死現(xiàn)象。RequireJS 是一個(gè) AMD 的實(shí)現(xiàn),后來(lái)還有CMD規(guī)范,玉伯的 而SeaJS 是 CMD 的一個(gè)實(shí)現(xiàn),兩者最大的區(qū)別就是

RequireJS 是預(yù)加載,而sea.js 是懶加載,也就是按需加載

什么意思 ??

比如下面的代碼

define(function(require,exports,module){
    // do something
    var mod1 = require('./mod1');
    // do something
    var mod2 = require('./mod2');})

RequireJS 會(huì)全部找到這個(gè)模塊的依賴(lài),并在開(kāi)始執(zhí)行是就加載全部的依賴(lài),
而SeaJS 則是按需加載,直到遇到 require 才會(huì)加載。

RequireJS 內(nèi)部通過(guò) Function.prototype.toString() ,然后使用正則匹配所有的require 方法,將其轉(zhuǎn)化為

define(['./mod1',./mod2']function(mod1,mod2)

這種方式。

有很多關(guān)于這兩種方式的爭(zhēng)吵,由于我沒(méi)有做過(guò)較大的項(xiàng)目,所以對(duì)于這兩種方式在正真項(xiàng)目中使用的區(qū)別也不清楚。

回顧 RequireJS

主要接口有兩個(gè): require & define,define 是模塊的定義方法,require 是模塊的使用方法。
define 的參數(shù)為 define (id?,deps?,factory)。第一個(gè)為模塊ID,第2個(gè)為依賴(lài)列表,第三個(gè)是工廠(chǎng)方法 。如果不定義ID,則為匿名方法,通常情況,模塊ID 等于模塊在工程的路徑。deps 和 factory 有約定,deps 數(shù)組有多少個(gè)元素,factory 就會(huì)有多少個(gè)形參,形參對(duì)于與依賴(lài)模塊工廠(chǎng)函數(shù)執(zhí)行后的返回值。

define ("id",["mod1","mod2"],function(mod1,mod2){
    return {
        ///
    };})

require 方法和 define 基本一致。

文件目錄

└─ use-require/ 
   ├─ app/ 
   │  ├─ js/ 
   │  │  ├─ module/ 
   │  │  │  ├─ a.js
   │  │  │  └─ b.js
   │  │  ├─ main.js
   │  └─ lib/  
   │  │  └─ require.js
   ├─ index.html
   └─ readme.md

現(xiàn)在,需要在 index.html 引導(dǎo)整個(gè)程序 。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Document</title></head><body>
     <script src="./app/lib/require.js" data-main="./app/js/main">
    </script></body></html>

引入 requirejs,并通過(guò) data-main 申明 啟動(dòng) js 文件。

注意: data-main 指向的文件所在路徑就是 baseUrl,在這里就是app/js ,
baseUrl 也可以通過(guò)require.config 設(shè)置 。如果沒(méi)有通過(guò) data-main 屬性指定 baseUrl ,也沒(méi)有通過(guò)config的方式顯示聲明 baseUrl ,那么 baseUrl 默認(rèn)為加載requirejs的那個(gè)頁(yè)面所在的路徑
下面 b 依賴(lài) a ,那么就應(yīng)該寫(xiě) module/a,而 main 依賴(lài) b ,寫(xiě)為 module/b ,這個(gè)路徑會(huì)和 baseUrl 拼接,與a ,b 所在的路徑無(wú)關(guān) 。有時(shí)候,你可能需要生成一個(gè)相對(duì)于模塊的URL地址。你可以將require作為一個(gè)依賴(lài)注入進(jìn)來(lái),然后調(diào)用require.toUrl()以生成該URL 。
define(["require"], function(require) { var url = require.toUrl("./style.js"); });

有時(shí)候你想避開(kāi)"baseUrl + paths"的解析過(guò)程,而是直接指定加載某一個(gè)目錄下的腳本。此時(shí)可以這樣做:如果一個(gè)module ID符合下述規(guī)則之一,其ID解析會(huì)避開(kāi)常規(guī)的"baseUrl + paths"配置,而是直接將其加載為一個(gè)相對(duì)于當(dāng)前HTML文檔的腳本:
注意是 相對(duì)于 HTML 文檔,因?yàn)?js腳本是會(huì)被插入到 html 文檔中執(zhí)行的 。

  • 以 ".js" 結(jié)束 。

  • 以 "/" 開(kāi)始。

  • 包含 URL 協(xié)議, 如 "http:" or "https:"。

一般來(lái)說(shuō),最好還是使用baseUrl及"paths" config去設(shè)置module ID。它會(huì)給你帶來(lái)額外的靈活性,如便于腳本的重命名、重定位等。 同時(shí),為了避免凌亂的配置,最好不要使用多級(jí)嵌套的目錄層次來(lái)組織代碼,而是要么將所有的腳本都放置到baseUrl中,要么分置為項(xiàng)目庫(kù)/第三方庫(kù)的一個(gè)扁平結(jié)構(gòu),如下:

└─ project/ 
   ├─ js/ 
   │  ├─ app/ 
   │  │  └─ sub.js
   │  ├─ lib/ 
   │  │  ├─ jquery.js
   │  │  └─ require.js 
   │  └─ app.js 
   ├─ index.html                           
   └─ readme.md

下面自然就順利成章
申明模塊a

define(function() {    'use strict';
    return {
        name: "hello , i am a"
    }})

模塊 b

define(["module/a"], function(a) {  'use strict';
    var aName = a.name;
    var name = "hello i am b";
    return {
        sayHello: function() {
            console.log(name + "  my brother is " + aName);
        }
    }})

在模塊b 里面依賴(lài)模塊a 。

main.js

require(["module/b"], function(b) {  'use strict';
    b.sayHello();})

看看效果
萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

如果使用 require.config() 配置呢?
將上面的目錄復(fù)制一份,命名 use-require-config,目錄結(jié)構(gòu)完全一致

main.js

requirejs.config({
    baseUrl: './app/js/module'})require([".b"], function(b) {    'use strict';
    b.sayHello();})

在這里,將配置放在 main.js 里面,等會(huì)兒會(huì)講如果不放在它里面會(huì)又什么問(wèn)題。

模塊 b 依賴(lài) 于 a

define(["./a"], function(a) {    'use strict';
    var aName = a.name;
    var name = "hello i am b";
    return {
        sayHello: function() {
            console.log(name + "  my brother is " + aName);
        }
    }})

由于將baseUrl 設(shè)置為 app/js/module ,所以這里依賴(lài)a 就可以直接寫(xiě)a
在這里,我使用 ./a, 與直接寫(xiě) a 一模一樣,還是使用 baseUrl+ moduleName `來(lái)尋找模塊路徑。
萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

如果不將 requirejs.config 放在main中,而是另外在引入一個(gè)script 節(jié)點(diǎn)來(lái)放置 呢

<body>
    <script src="./app/lib/require.js" data-main="./app/js/main">
    </script>
    <script>
        requirejs.config({
           baseUrl: './app/js/module'
        })    </script></body>

在瀏覽器運(yùn)行
萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

發(fā)現(xiàn)報(bào)錯(cuò)了,怎么回事呢?
看它尋找 main.js 的路徑

file:///E:/HFLib/module/code/use-require-config/app/js/module/main.js

是 baseUrl + main.js。 這說(shuō)明,配置的 baseUrl 覆蓋了為 data-main 配置的路徑
也就是說(shuō),一旦使用requirejs.config 來(lái)配置 baseUrl,那么所有的路徑都會(huì)以 baseUrl 為基準(zhǔn)。
所以,data-main 應(yīng)改為: ..\main,注意這里必須要使用反斜杠,正斜杠拼接總是會(huì)出現(xiàn)問(wèn)題。
通過(guò)這樣就可以解決 data-main 和 requirejs.config() 的沖突了 。

paths 和 shim 。

當(dāng)模塊名過(guò)長(zhǎng)是,require.js 為我們提供了路徑 paths 的方式 。
理想狀況下,每個(gè)加載的腳本都是通過(guò)define()來(lái)定義的一個(gè)模塊;但有些"瀏覽器全局變量注入"型的傳統(tǒng)/遺留庫(kù)并沒(méi)有使用define()來(lái)定義它們的依賴(lài)關(guān)系,你必須為此使用shim config來(lái)指明它們的依賴(lài)關(guān)系。

這是我的文件目錄
萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

index.html

<body>
    <script src="./app/lib/require.js" data-main="./app/main">
    </script></body>

main.js

requirejs.config({
    paths: {
        jQuery: 'lib/jQuery'
    },
    shim: {
        'jQuery': {
            exports: '$'
        }
    }})require(['jQuery'], function($) {    'use strict';
    console.log($);})

萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

可以看到,已經(jīng)順利注入了 jQuery 依賴(lài)。

require.js 中還有很多配置和用法,我用的就比較少,就簡(jiǎn)單介紹到這里。
下來(lái),我要實(shí)現(xiàn)一個(gè)簡(jiǎn)化的模塊加載器,類(lèi)似于 require.js.

實(shí)現(xiàn)一個(gè)自己的模塊加載器