標籤:fun 開發人員 efi src 開發 script fine red blank
requirejs、require方法衝突
如果載入了多個requirejs指令碼,每個requirejs會判斷是否瀏覽器已經實現了require和define方法。如果瀏覽器已經內建require和define方法,或者之前已經有一個requirejs指令碼執行,那麼這個requirejs就會立刻停止執行。所以,即使頁面上載入了多次requirejs指令碼也不會有什麼問題。
配置Context
我把context叫做一個命名空間,因為每一個context都有一個名字,這樣同名而功能不同的模組就可以放在不同的context中以防衝突。
如果開發人員沒有配置context,那麼,requirejs還會產生一個預設的context,這個預設的context配置大致如下:
requirejs.config({
context: "_", // default context name
baseUrl: "./",
waitSeconds:7, // how long canloading last
paths: {},
bundles: {},
pkgs: {},
shim: {},
config: {}
});
注意:在不指定context名稱的情況下,任何配置和調用都是針對預設context的修改和調用。
空間名稱 – context
如果requirejs初始化時自訂配置context,那麼預設建立的context的name 就是”_”。如果需要添加新的context,只需指定一個新的contextName即可,比如下面這個調用就會建立一個新的context:
requirejs({context:”new content name”});
同名的context只會有一個,配置同名的context等於修改這個context的屬性。
載入逾時 – waitSeconds
每個context都配置了一個載入逾時的時間,某個模組如果沒有初始化,載入的時間又超過了這個時間,就會被認為載入失敗。
載入逾時是針對整個context下的所有模組而言的,而不是單指某個模組,也就是說這個預設的7秒是指所有模組應該在7秒之內全部載入完成。7秒之後,如果有沒有被載入的模組,將拋出error指示哪些模組沒有載入。(requirejs每隔50毫秒做一次判斷)。
基準URL – baseUrl
每個context的基準URL預設值是”./”。
第一個context的基準URL
如果開發人員沒有指定context名稱,那麼這個第一個context就是requirejs預設產生的context,否則就是開發人員自己定義的context。在不指定基準URL的前提下,第一個context的基準URL設定比較特殊,除了標準的設定方法(參考後面的基準URL標準設定方法),還可以使用以下兩種特殊方式設定:
第一種:通過requirejs或require對象配置
在確認requirejs指令碼之前沒有其它requirejs執行過的前提下:
<script>requirejs={baseUrl: ‘./‘}</script>
<script data-main="scripts/app.js"src="../require.js"></script>
或
<script>require={baseUrl: ‘./‘}</script>
<script data-main="scripts/app.js"src="../require.js"></script>
注意:通過這種方式設定基準URL,data-main指定的指令檔位置也會變成相對於基準URL的路徑,因為data-main指定的指令碼本身只是依賴的關係之一。而且,data-main指定的指令碼也屬於第一個context。
比如下面這種情況:
<script>requirejs={baseUrl: ‘scripts/lib‘}</script>
<script data-main="scripts/app.js"src="../require.js"></script>
指令碼模組app.js的最終路徑變成了"scripts/lib/scripts/app.js",不是原來的"scripts/app.js",而且它的依賴名稱也會變為scripts/lib/scripts/app(requirejs預設會去掉指令碼路徑的最後一個“.js”,除非data-main的值以“/”開頭,或包含“:”,或包含“?”)。
第二種:根據script元素的data-main屬性指定的指令碼路徑計算
如果沒有設定baseUrl,requirejs會根據script元素data-main屬性指定的JavaScript檔案路徑計算出一個基準URL。
比如data-main="scripts/app.js",那麼baseUrl就是"scripts/":
<script data-main="scripts/app.js"src="../require.js"></script>
基準URL標準設定方法
除了第一個context可以使用上面的方法,其它自訂的context配置baseUrl就只能使用下面這兩種方法。但這兩種方法同樣也可以用來修改第一個context的屬性,在不指定context名稱的情況下,其實就是修改第一個context。
通過requirejs或require方法(這兩個本身就是同一個方法)設定
以下兩個等於把預設命名空間的基準URL設定成了scripts/lib:
requirejs({baseUrl:‘scripts/lib‘});
require({baseUrl:‘scripts/lib‘});
通過requirejs.config方法
requirejs.config({baseUrl:‘scripts/lib‘});
require.config({baseUrl:‘scripts/lib‘});
其實config方法調用的就是requirejs方法,所以它們是一樣的。
模組依賴 – deps
模組依賴是指個模組之間的相互依賴關係,指令碼運行時,只有當依賴的模組全部載入完成之後,當前指令碼才會執行,這就是依賴關係的作用。
依賴關係使用數組配置,數組元素為字串(即模組的名稱),一般是相對於baseUrl的路徑,只不過沒有檔案尾碼。而且,為了比較方便的擷取模組入口,模組一般會通過define方法定義。因為,通過define定義的模組,可以被依賴數組後面的回呼函數直接擷取並使用。
以jQuery為例,在jquery指令碼的末尾一般有下面兩行代碼:
if(typeof define === "function"&& define.amd && define.amd.jQuery) {
define("jquery", [], function () { return jQuery; } );
}
再以underscore為例,在指令碼末尾有下面幾行代碼:
if (typeof define === ‘function‘ && define.amd) {
define(‘underscore‘, [],function() {
return _;
});
}
模組和模組位置
使用require配置相依模組的時候,只是聲明了模組的名稱,卻不知道模組的具體位置。在沒有特殊聲明的情況下,requirejs認為模組名和檔案名稱相同,因此,只要兩者一致,requirejs就可以正確找到指令檔。但如果不同,就需要通過path配置:
requirejs.config({
baseUrl:"scripts/lib",
paths: {
jquery:‘jquery-1.7.2‘
}
});
這樣,requirejs就知道jquery模組位於scripts/lib/jquery-1.7.2.js檔案中。
第一個context的依賴關係
<script>requirejs={deps: [‘jquery‘]}</script>
<script data-main="scripts/app.js"src="../require.js"></script>
其它context的依賴關係
與基準URL的方式一樣,既可以通過requirejs方法,也可通過requirejs.config方法配置。
模組束-bundles
如果一個JS檔案中有多個模組,就可以使用模組束的方式聲明:
requirejs.config({
baseUrl:"scripts/lib",
bundles: {
jsUtil:[‘MathUtil‘, ‘DateUtil‘]
}
});
上面這個例子就是說在scripts/lib/jsUtils.js檔案中有MathUtil和DateUtil這兩個子模組。
JS包– packages
如果一個檔案夾中有多個JS檔案,使用path的方式寫全就需要很多行代碼,這個時候如果使用包的方式聲明就可以省去很多麻煩:
requirejs.config({
baseUrl:"scripts/lib",
pkgs: [{name:‘jqueryui‘,location: ‘jqueryui/‘,main: ‘core‘}]
});
這樣定義之後,凡是在scripts/lib/jqueryui/目錄下的模組就可以通過這種方式正確找到:
require([‘jqueryui/button‘, ‘jqueryui/dialog‘]);
上面這個例子就是擷取scripts/lib/jqueryui/button.js和scripts/lib/jqueryui/dialog.js的例子。另外,因為jqueryui是一個目錄,並不對應一個JS檔案,所以又有一個main屬性,這個屬性一般對應這個JS包中的主程式檔案。上面的例子中,jqueryui的主程式就在scripts/lib/jqueryui/core.js中。
楔子 – shim
並不是所有的JS模組都會像jquery和underscore那樣調用define方法定義自己,這樣requirejs就不知道你這個模組的入口在哪,該通過哪個對象來調用這個模組,特別是那些早版本的JS模組,因為那是還沒有define和require的概念。
requirejs.config({
baseUrl:"scripts/lib",
shim: {jquery: {deps:[],exportsFn: func, exports:‘jQuery‘,init:func}}
});
雖然模組沒有使用define方法定義自己,但開發人員應該是知道如何擷取檔案中的模組的,所以,requirejs提供了兩種方式讓開發人員把模組對象返回給requirejs管理:
- 在exportsFn或init方法中設定,然後作為傳回值;
- 使用exports設定,比如”a.b.c”,那requirejs就知道通過window.a.b.c可以擷取。
映射 – Map
先來看問題:一些第三方JS外掛程式的依賴關係是事先設定好的,不太好修改相依模組的名稱,而如果某個模組有多個版本或有其他模組和它同名,則使用上面的配置都無法解決問題。比如path只是解決模組名稱到路徑的問題,而這個面對的是切換模組名稱的問題。於是requirejs提出了映射的概念,根據當前指令碼的名稱動態修改所相依模組的ID,是它指向正確的模組。
假如在你的硬碟下有以下幾個模組:
- foo1.0.js
- foo1.2.js
- some/newmodule.js
- some/oldmodule.js
在newmodule.js和oldmodule.js中都有require(‘foo’)調用,要解決衝突只需要這樣配置即可:
requirejs.config({
map: {
‘some/newmodule‘:{
‘foo‘: ‘foo1.2‘
},
‘some/oldmodule‘:{
‘foo‘:‘foo1.0‘
}
}
});
主程式入口data-main
<script data-main="scripts/app.js"src="../require.js"></script>
不管頁面上有多少個script元素有data-main屬性,requirejs只認最後一個script元素的data-main屬性,忽略其他script元素的data-main屬性。
Requirejs擷取data-main屬性之後,並沒有立即執行data-main指定的指令檔(因為這個指令檔可能還依賴了其他模組),而是把它作為了一個被依賴的模組,加入到第一個context的依賴數組中。比如下面這種情況就是把scripts/app這個模組加到一個名叫linus的context中:
<script type="text/javascript">
requirejs={
context: ‘linus‘,
baseUrl:"./",
skipDataMain: false
};
</script>
<script data-main="scripts/app.js"src="../require.js"></script>
全域配置忽略script元素的data-main
在瀏覽器中,有一個選項叫skipDataMain,可以讓requirejs忽略script元素的data-main。在預設情況下,requirejs成功載入之後,會立馬尋找頁面上所有script元素,並且把最後一個有data-main屬性的script元素的data-main最為主程式入口。
<script>requirejs={skipDataMain:true}</script>
<scriptdata-main="scripts/app.js"src="../require.js"></script>
模組定義 – define(name, deps, callback)
你會發現define方法沒有指定context名稱,這是因為define方法只調用於被依賴的模組中,而require方法已經為依賴的模組指定了context名稱,所以,這個模組被哪個context需要,它就屬於那一個context。
參數name是模組的名稱,deps是該模組所依賴的其他模組的名稱,callback一般返回該模組的實際可被使用對象。比如jQuery的模組定義回呼函數返回的就是jQuery對象。
Error載入Error
這種error就是瀏覽器內建的Error對象,只不過requirejs給它附加了其他屬性。
- message的格式為:msg + ‘\nhttp://requirejs.org/docs/errors.html#‘ + id。
- error.requireType就是就是message後面的id;
- error.requireModules一般指需要載入卻沒載入成功的模組名稱;
- error.originalError是指發生其他錯誤導致模組載入失敗的原始error對象。
Requirejs常用配置和應用