使用RequireJS並實現一個本身的模塊加載器 (一)

RequireJS & SeaJS

模塊化開發 開發之前,都是直接在頁面上引入 script 標籤來引用腳本的,當項目變得比較複雜,就會帶來不少問題。javascript

  • JS項目中的依賴只有經過引入JS的順序來肯定,項目會變得複雜難以維護。
  • 複雜的腳本會暴露不少全局變量, 好比 $,_. ... 。
  • 同步加載的時候,網頁會中止渲染,加載時間越長,網頁失去響應的事件就越長。

因而,AMD 規範就誕生了,AMD 即爲異步模塊定義,有效避免同步加載致使頁面的假死現象。RequireJS 是一個 AMD 的實現,後來還有CMD規範,玉伯的 而SeaJS 是 CMD 的一個實現,二者最大的區別就是html

RequireJS 是預加載,而sea.js 是懶加載,也就是按需加載java

什麼意思 ??jquery

好比下面的代碼數組

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

RequireJS 會所有找到這個模塊的依賴,並在開始執行是就加載所有的依賴,
而SeaJS 則是按需加載,直到遇到 require 纔會加載。瀏覽器

RequireJS 內部經過 Function.prototype.toString() ,而後使用正則匹配全部的require 方法,將其轉化爲app

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

這種方式。異步

有不少關於這兩種方式的爭吵,因爲我沒有作過較大的項目,因此對於這兩種方式在正真項目中使用的區別也不清楚。模塊化

回顧 RequireJS

主要接口有兩個: require & define,define 是模塊的定義方法,require 是模塊的使用方法。
define 的參數爲 define (id?,deps?,factory)。第一個爲模塊ID,第2個爲依賴列表,第三個是工廠方法 。若是不定義ID,則爲匿名方法,一般狀況,模塊ID 等於模塊在工程的路徑。deps 和 factory 有約定,deps 數組有多少個元素,factory 就會有多少個形參,形參對於與依賴模塊工廠函數執行後的返回值。函數

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

如今,須要在 index.html 引導整個程序 。

<!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,並經過 data-main 申明 啓動 js 文件。

注意: data-main 指向的文件所在路徑就是 baseUrl,在這裏就是app/js ,
baseUrl 也能夠經過require.config 設置 。若是沒有經過 data-main 屬性指定 baseUrl ,也沒有經過config的方式顯示聲明 baseUrl ,那麼 baseUrl 默認爲加載requirejs的那個頁面所在的路徑
下面 b 依賴 a ,那麼就應該寫 module/a,而 main 依賴 b ,寫爲 module/b ,這個路徑會和 baseUrl 拼接,與a ,b 所在的路徑無關 。有時候,你可能須要生成一個相對於模塊的URL地址。你能夠將require做爲一個依賴注入進來,而後調用require.toUrl()以生成該URL 。
define(["require"], function(require) { var url = require.toUrl("./style.js"); });

有時候你想避開"baseUrl + paths"的解析過程,而是直接指定加載某一個目錄下的腳本。此時能夠這樣作:若是一個module ID符合下述規則之一,其ID解析會避開常規的"baseUrl + paths"配置,而是直接將其加載爲一個相對於當前HTML文檔的腳本:
注意是 相對於 HTML 文檔,由於 js腳本是會被插入到 html 文檔中執行的 。

  • 以 ".js" 結束 。
  • 以 "/" 開始。
  • 包含 URL 協議, 如 "http:" or "https:"。

通常來講,最好仍是使用baseUrl及"paths" config去設置module ID。它會給你帶來額外的靈活性,如便於腳本的重命名、重定位等。 同時,爲了不凌亂的配置,最好不要使用多級嵌套的目錄層次來組織代碼,而是要麼將全部的腳本都放置到baseUrl中,要麼分置爲項目庫/第三方庫的一個扁平結構,以下:

└─ 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 裏面依賴模塊a 。

main.js

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

看看效果

若是使用 require.config() 配置呢?
將上面的目錄複製一份,命名 use-require-config,目錄結構徹底一致

main.js

requirejs.config({
    baseUrl: './app/js/module'
})

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

在這裏,將配置放在 main.js 裏面,等會兒會講若是不放在它裏面會又什麼問題。

模塊 b 依賴 於 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 設置爲 app/js/module ,因此這裏依賴a 就能夠直接寫a
在這裏,我使用 ./a, 與直接寫 a 如出一轍,仍是使用 baseUrl+ moduleName `來尋找模塊路徑。

若是不將 requirejs.config 放在main中,而是另外在引入一個script 節點來放置 呢

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

在瀏覽器運行

發現報錯了,怎麼回事呢?
看它尋找 main.js 的路徑

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

是 baseUrl + main.js。 這說明,配置的 baseUrl 覆蓋了爲 data-main 配置的路徑
也就是說,一旦使用requirejs.config 來配置 baseUrl,那麼全部的路徑都會以 baseUrl 爲基準。
因此,data-main 應改成: ..\main,注意這裏必需要使用反斜槓,正斜槓拼接老是會出現問題。
經過這樣就能夠解決 data-main 和 requirejs.config() 的衝突了 。

paths 和 shim 。

當模塊名過長是,require.js 爲咱們提供了路徑 paths 的方式 。
理想情況下,每一個加載的腳本都是經過define()來定義的一個模塊;但有些"瀏覽器全局變量注入"型的傳統/遺留庫並無使用define()來定義它們的依賴關係,你必須爲此使用shim config來指明它們的依賴關係。

這是個人文件目錄

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($);
})

能夠看到,已經順利注入了 jQuery 依賴。

require.js 中還有不少配置和用法,我用的就比較少,就簡單介紹到這裏。
下來,我要實現一個簡化的模塊加載器,相似於 require.js.

實現一個本身的模塊加載器

太晚了 。

就在 實現一個本身的模塊加載器(二)中實現一個相似 require.js 的Demo 吧 。

相關文章
相關標籤/搜索