接觸過requirejs的童鞋可能都知道,無論是通過define來定義模塊,還是通過require來加載模塊,模塊依賴聲明都是很重要的一步。而其中涉及到的模塊路徑解析,對于新手來說,有的時候會讓人覺得很困惑。
start up
假設我們的目錄結構如下:
demo.html
js/main.js
js/lib.js
js/util.js
js/common/lib.js
js/common/jqury/lib.js
common/lib.js
下面的這兩個例子,看著很簡單吧,但應該大部分的人跟我一樣沒辦法一眼就識別出來依賴模塊最終轉化成的路徑。原因在于,這里壓根就沒有提供足夠的上下文信息。。。(= =b 別打我)
require例子:
// main.js
require(['./util'], function(){
// do sth
});
define例子:
define(['./util'], function(){
// do sth
});
下面,我們再一步步通過具體的例子來看下,requirejs在不同的場景下,是如何解析模塊路徑的。
baseUrl
:基礎中的基礎
在requirejs的模塊路徑解析里,baseUrl
是非常基礎的概念,離開了它,基本就玩不轉了,所以這里簡單介紹一下。簡單的說,baseUrl
指定了一個目錄,然后requirejs基于這個目錄來尋找依賴的模塊。
舉個栗子,在demo.html里加載requirejs,同時在requirejs所在的script上聲明data-main
屬性,那么,requirejs加載下來后,它會做兩件事件:
- 加載js/main.js
- 將baseUrl設置為data-main指定的文件所在的路徑,這里是 js/
<script src="js/require.js" data-main="js/main.js"></script>
那么,下面依賴的lib模塊的實際路徑為 js/lib.js
main.js
require(['lib'], function(Lib){
// do sth
});
當然,除了data-main
屬性,你也可以手動配置baseUrl
,比如下面例子。需要強調的是:
如果沒有通過
data-main
屬性指定baseUrl
,也沒有通過config的方式顯示聲明baseUrl
,那么baseUrl
默認為加載requirejs的那個頁面所在的路徑
demo.html
<script src="js/require.js"></script>
<script src="js/main.js"></script>
main.js
requirejs.config({
baseUrl: 'js'
});
require(['lib'], function(Lib){
// do sth
});
baseUrl
+ path
:讓依賴更簡潔、靈活
比如我們加載了下面一堆模塊(好多水果。。。),看著下面一長串的依賴列表,可能你一下子就看出問題來了:
- 費力氣:每個加載的模塊前面都有長長的
common/fruits
- 難維護:說不定哪一天目錄名就變了(在大型項目中并不算罕見),想象一下目錄結構變更帶來的工作量
requirejs.config({
baseUrl: 'js'
});
// 加載一堆水果
require(['common/fruits/apple', 'common/fruits/orange', 'common/fruits/grape', 'common/fruits/pears'], function(Apple, Orange, Grape, Pears){
// do sth
});
對一個模塊加載器來說,上面說的這兩點問題顯然需要考慮進去。于是requirejs的作者提供了paths
這個配置項。我們看下修改后的代碼。
requirejs.config({
baseUrl: 'js',
paths: {
fruits: 'common/fruits'
}
});
// 加載一堆水果
require(['fruits/apple', 'fruits/orange', 'fruits/grape', 'fruits/pears'], function(Apple, Orange, Grape, Pears){
// do sth
});
其實就少了個common
前綴,也沒節省多少代碼,但當項目結構變更時,好處就體現了。假設common/fruits
某一天突然變成了common/third-party/fruits
,那很簡單,改下paths
就可以了。
requirejs.config({
baseUrl: 'js',
paths: {
fruits: 'common/third-party/fruits'
}
});
paths
:簡單但需要記住的要點
上一節已經舉例說明了path的例子。這里再來個例子,說明下下三種情況下,匹配路徑的規則
>
apple
:沒有在paths規則里定義,于是為 baseUrl + apple.js => js/apple.jscommon/fruits
:common已經在paths里定義,于是為baseUrl + common/fruits + apple.js => js/common/fruits/apple.js../common/apple
:common盡管已經在paths里定義,但是../common/apple
并不是以common開頭,于是為 baseUrl + ../common/apple.js => common/apple.js
requirejs.config({
baseUrl: 'js',
paths: {
common: 'common/fruits'
}
});
// 從左到右,加載的路徑依次為 js/lib.js、 js/common/jquery/lib.js、common/lib.js
require(['apple', 'common/apple', '../common/apple'], function(){
// do something
});
./module
:讓人疑惑的相對路徑
應該說,這個是最讓人疑惑的地方。
demo 1
js/main.js
requirejs.config({
baseUrl: 'js/common'
});
// 實際加載的路徑都是是 /lib.js
require(['./lib', 'lib'], function(Lib){
Lib.say('hello');
});
demo 2
簡單改下上面的例子,可以看到:
通過
define
定義模塊A時,模塊A依賴的模塊B,如果是./module
形式,則基于模塊A所在目錄解析模塊B的路徑。
js/main.js
requirejs.config({
baseUrl: 'js'
});
// 依賴lib.js,實際加載的路徑是 js/common/lib.js,而lib模塊又依賴于util模塊('./util'),解析后的實際路徑為 js/common/util.js
require(['common/lib'], function(Lib){
Lib.say('hello');
});
js/lib.js
// 依賴util模塊
define(['./util'], function(Util){
return {
say: function(msg){
Util.say(msg);
}
};
});
demo 3
demo2實際上會有特例,比如下面,lib模塊依賴的util模塊,最終解析出來的路徑是js/util.js
main.js
requirejs.config({
baseUrl: 'js',
paths: {
lib: 'common/lib'
}
});
// 實際加載的路徑是 js/common/lib.js
require(['lib'], function(Lib){
Lib.say('hello');
});
lib.js
// util模塊解析后的路徑為 js/util.js
define(['./util'], function(Lib){
return {
say: function(msg){
Lib.say(msg);
}
};
});
demo 4
上面講到通過paths指定的模塊路徑加載模塊時,./module
路徑解析就會按照baseUrl
+ moduleName
的方式,但稍微修改下main.js,發現結果就不一樣了。此時,util模塊對應的路徑為js/common/util.js
main.js
requirejs.config({
baseUrl: 'js',
paths: {
common: 'common'
}
});
// 實際加載的路徑是 js/common/lib.js
require(['common/lib'], function(Lib){
Lib.say('hello');
});
util.js
define(['./util'], function(Lib){
return {
say: function(msg){
Lib.say(msg);
}
};
});
各種疑問
為什么require(['./module'], callback)不是相對于當前路徑解析
如下面例子所示,我們可能會疑惑,為什么不是相對于main.js
所在的路徑解析呢?其實很簡單,作者也不知道你這段代碼出現在哪個文件呀親。所以,只能通過baseUrl
js/main.js
require(['./lib', function(Lib){
// do sth
}]);
define(['./module'], callback)什么時候是相對當前路徑
加個@todo,這個估計只有作者和看過源碼的人知道了,好像文檔里也沒明確說到~todo下先
// @todo
寫在后面
啰啰嗦嗦寫了一大堆,requirejs中的路徑解析整體上不復雜,但./module
這種形式的路徑解析,對于剛接觸requirejs的人來說稍微有些費解。也許,當你從requirejs設計者的角度來看,問題可能相對好理解一些。
文章列表