requirejs:模塊加載(require)及定義(define)時的路徑小結

原文地址:http://www.tuicool.com/articles/7JBnmyjavascript

接觸過requirejs的童鞋可能都知道,不管是經過define來定義模塊,仍是經過require來加載模塊,模塊依賴聲明都是很重要的一步。而其中涉及到的模塊路徑解析,對於新手來講,有的時候會讓人以爲很困惑。css

假設咱們的目錄結構以下:html

 

demo.html 
js/main.js 
js/lib.js 
js/util.js 
js/common/lib.js 
js/common/jqury/lib.js 
common/lib.jsjava

 

下面的這兩個例子,看着很簡單吧,但應該大部分的人跟我同樣沒辦法一眼就識別出來依賴模塊最終轉化成的路徑。緣由在於,這裏壓根就沒有提供足夠的上下文信息。。。(= =b 別打我)jquery

require例子:sql

// main.js require(['./util'], function(){ // do sth }); 

define例子:ruby

define(['./util'], function(){ // do sth }); 

下面,咱們再一步步經過具體的例子來看下,requirejs在不一樣的場景下,是如何解析模塊路徑的。app

baseUrl :基礎中的基礎

在requirejs的模塊路徑解析裏, baseUrl 是很是基礎的概念,離開了它,基本就玩不轉了,因此這裏簡單介紹一下。簡單的說, baseUrl 指定了一個目錄,而後requirejs基於這個目錄來尋找依賴的模塊。requirejs

舉個栗子,在demo.html里加載requirejs,同時在requirejs所在的script上聲明data-main 屬性,那麼,requirejs加載下來後,它會作兩件事件:ui

  1. 加載js/main.js
  2. 將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 :讓依賴更簡潔、靈活

好比咱們加載了下面一堆模塊(好多水果。。。),看着下面一長串的依賴列表,可能你一會兒就看出問題來了:

  1. 費力氣:每一個加載的模塊前面都有長長的 common/fruits
  2. 難維護:說不定哪一天目錄名就變了(在大型項目中並不算罕見),想象一下目錄結構變動帶來的工做量
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的例子。這裏再來個例子,說明下下三種狀況下,匹配路徑的規則 
>

  1. apple :沒有在paths規則裏定義,因而爲 baseUrl + apple.js => js/apple.js
  2. common/fruits :common已經在paths裏定義,因而爲baseUrl + common/fruits + apple.js => js/common/fruits/apple.js
  3. ../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設計者的角度來看,問題可能相對好理解一些

相關文章
相關標籤/搜索