github地址:
https://github.com/requirejs/requirejs
好的文章:https://www.jianshu.com/p/8687f539642c
require.js 是一個JavaScript文件和模塊加載器,也是模塊管理工具
requireJs 作用是什麼,優勢是什麼? 它解決什麼難題?
1. 有效防止了命名衝突
2. 聲明瞭不同 JS 文件之間的依賴關係
3. JS代碼以模塊化的 方式組織。
requireJs 爲解決前端代碼庫的組織難題,它的解決方案是:
模塊化組織js文件
異步加載js文件
JavaScript模塊化編程
JavaScript模塊化編程的目的是爲了讓開發者 僅需實現核心的業務邏輯,其他的都加載別人寫好的模塊就行。
但是JavaScript並不是模塊化的語言,ES6才正式支持類和模塊,之前的版本不支持類,更不用說module.
什麼是模塊?
模塊的原始寫法:
將不同函數及變量簡單的放在一起,看作一個模塊。
var a = xxx;
function func1(){...}
function func2(){...}
缺點是:污染全局變量,無法保證 不與其他模塊發生命名衝突。而且不能清晰的看出模塊成員之間關係;
模塊的對象寫法
將模塊定義爲一個對象,所有模塊成員放在對象裏邊。
//將屬性和操作都封裝在對象中
var module = new Object({
_prop:11,
func1:function(){...}
func2:function(){...}
})
//使用時直接調用對象的屬性;
module.func1();
確定:暴露了模塊成員,內部狀態 可以被外部改寫。
module._prop = 100;
立即執行函數寫法:
IIFE( Immediately-Invoked Function Express) 可以達到不暴露私有成員的目的
var module = (function(){
var _prop = 0;
var fn1 = function(){...};
var fn2 = function(){...};
return {fn1:fn1, fn2:fn2};
})();
放大模式/ 繼承模式 (augmentation).
如果一個模塊很大,必須被分成幾個部分,或者一個模塊需要繼承另外一個模塊。
var module = (function(mod){
mod.fn = function(){...};
return mod;
})(module);
寬放大模式(Loose Augmentation): 放大模式的改進
瀏覽器環境中 各個模塊 通常都是通過網絡獲取的。採用放大模式,第一個執行的部分 可能使用一個不存在的空對象,
此時需要要使用寬放大模式
var module = (function(mod){
mod.fn = function(){...};
return mod;
})(window.module || {});
如何保證模塊的獨立性?如何使用全局變量(其他模塊)?
獨立性是模塊的重要特點,模塊內部最好不要與程序的其他模塊直接交互。爲了在模塊內部使用全局變量,
必須顯示地將其他變量 作爲參數輸入模塊
var module = (function($){
})(jQuery);
AMD規範
Js目前沒有官方規範,通用的JS 模塊規範有2種,CommonJS和AMD
CommonJS
老實說在瀏覽器環境下,沒有模塊並不是特別大的問題,畢竟網頁的複雜性有限。但是對於服務端編程,
一定要有模塊,否則無法編程。
2009年,美國程序員Ryan Dahl創建了NodeJS項目,將JS用於服務端編程。由此標誌着JS模塊化編程的正式誕生。
NodeJS的模塊系統是參照CommonJS規範實現的,在CommonJS中有一個全局方法require()
,用於加載模塊。
var match = require("math");
match.add(1,2)
由於一個重大的侷限,使得CommonJS規範不適用於瀏覽器環境。問題是對於服務器而言模塊都放在本地,可同步加載等待時間只是硬盤讀取時間。但是當瀏覽器中使用服務端的模塊時,等待時間取決於網速快慢,長時間的等待會造成瀏覽器處於“假死”狀態。
因此瀏覽器端的 模塊 不能使用“同步加載(synchronous)", 只能採用“異步加載(asynchronous)"方式,
這就是AMD規範誕生的背景。
AMD (Asynchronous Module Definition) 異步模塊加載。
所有依賴模塊的語句都定義在一個回調函數中,等到加載完成後,回調函數纔會執行。
AMD也採用了require()
語句加載模塊,不同於CommonJS的是,它要求兩個參數。
// module參數爲一個數組,裏面的成員是要加載的模塊
// callback參數是模塊加載成功後執行的回調函數
require([module], callback)
// math模塊與math.add()加載不是同步的,瀏覽器不會發生假死,因此AMD比較適合瀏覽器環境。
require(["math"], function(math){
math.add(1, 2);
});
RequireJS
加載資源文件
<script src="https://cdn.bootcss.com/require.js/2.3.5/require.js"></script>
在引入require.js文件之後,整個windows 對象就有 require()方法。可以通過require()方法來加載其他JS文件。
RequireJS的入口是 引入時指定的data-main屬性,在require.js引入後,會自動執行指向data-main屬性所指定的入口文件。
由於引入requireJS本身可能會造成頁面失去響應,解決的方式可將其放在網頁底部加載。或使用延遲加載。
<script src="./assets/scripts/require-2.3.5.js" data-main="js/main" async="true" defer></script>
主模塊:
data-main
加載的是主模塊
baseUrl 可通過 requirejs.config手動設置,若沒有顯示指定config及data-main,則默認的baseUrl爲包含requireJS的
那個html頁面所屬的目錄。
$ vim js/main.js
/**
* RequireJS全局配置文件
*/
requirejs.config({
//設置項目路徑,項目會以baseUrl作爲相對路徑去查找模塊文件
baseUrl:"./js",
//預加載JS文件的配置項,默認可不用添加.js後綴
paths:{
//RequireJS默認假定所有的依賴資源都是JS腳本,因此無需再module ID上再加上js後綴。
jquery:"../scripts/jquery-3.3.1"
}
});
正常情況下,主模塊是依賴其他模塊的,此時要使用AMD規範定義的require()函數。
require([module], function(module){...});
/**
* RequireJS全局配置文件
*/
require.config({
//設置項目路徑,項目會以baseUrl作爲相對路徑去查找模塊文件
baseUrl:"./js",
//預加載JS文件的配置項,默認可不用添加.js後綴
paths:{
//RequireJS默認假定所有的依賴資源都是JS腳本,因此無需再module ID上再加上js後綴。
jquery:"https://cdn.bootcss.com/jquery/3.3.1/jquery",
bootstrap:"https://cdn.bootcss.com/bootstrap/4.1.1/js/bootstrap"
}
});
require(['jquery', 'bootstrap'],function($, undefined){
});
RequireJS要求每個模塊是一個單獨的JS文件,如果多加載幾個模塊會發出多次HTTP請求,
實際上,雖然部分的函數庫符合AMD規範,但更多的庫並不符合。
RequireJS 如何加載非規範的模塊呢?
在使用require()
之前,需在require.config()
函數中定義非規範模塊的特徵。
require.config()
接收一個配置對象,此對象除了paths
屬性之外,還有一個shim
屬性,專門用來配置不兼容的模塊。每個模塊需要定義exports
值即輸出的變量名,表明這個模塊外部調用名稱。其次deps
數組屬性表明該模塊的依賴性。
RequireJS源碼解析
requireJS工作流程
- 載入模塊
- 通過模塊名解析出模塊信息並計算出URL
- 通過創建
script
的形式將模塊加載到頁面 - 判斷被加載腳本若存在依賴則加載,若不存在則直接執行
factory()
。 - 等待所有腳本都加載完畢後執行回調函數