【requireJS路徑加載】與程序員小卡的交流

這兩天正好看到了程序員小卡同窗的一篇博客,裏面對requireJS路徑的解析作了一些說明,裏面有點問題待解決,我這裏正好知道一點,因此整理成文,不知對小卡同窗是否有幫助。html

 
首先以其例子爲例:
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');
});
// 依賴util模塊
define(['./util'], function(Util){
    return {
        say: function(msg){
            Util.say(msg);
        }
    };
});

如果變個寫法,util的目錄結構就變了node

requirejs.config({
    baseUrl: 'js',
    paths: {
        lib: 'common/lib'
    }
});

// 實際加載的路徑是 js/common/lib.js
require(['lib'], function(Lib){
    Lib.say('hello');
});
// util模塊解析後的路徑爲 js/util.js
define(['./util'], function(Lib){
    return {
        say: function(msg){
            Lib.say(msg);
        }
    };
});

咱們今天便一塊兒來學習下這個問題程序員

requireJS的basePath

對於baseUrl的解析須要注意,當知足如下條件,將不會相對baseUrl
① 以"/"開頭
② 以".js"結尾
③ 包含各類協議
不出現以上條件,設置的path,是相對於baseUrl的

簡單requireJS流程

而後咱們這裏再簡單的整理下requireJS這部分的流程:
① 經過require加載主幹流程,這裏就是咱們所謂的入口,以上述代碼爲例,入口是:
require(['common/lib'], function(Lib){
    Lib.say('hello');
});

該代碼會在require內部執行過程當中,具備第一個依賴項,這個依賴項是'common/lib',他的鍵值即是這個了app

這裏會首先加載器依賴項,common/lib,而此時便會作第一步的解析而且造成一個模塊ide

在模塊加載時,會建立一個script標籤,而且爲其綁定load事件,這裏會有第二個事件的觸發oop

② 在加載common/lib模塊時,有一個關鍵點須要注意:requirejs

  • 文件加載結束便會立刻執行,因此其define方法執行了,而且往globalDefQueue寫入了數據
  • load事件觸發,會建立一個requireJS module,這個時候其依賴項會加載

上述雖然與本次討論的東西無關,倒是理解整個require的關鍵,各位能夠去看看學習

③ context.completeLoad(data.id) =>可是這個時候卻發現其有一個依賴項,因而便會先加載器依賴項,這裏又會進入,main.js中require的邏輯,即這段代碼:ui

 1 //Enable each dependency
 2 each(this.depMaps, bind(this, function (depMap, i) {
 3   var id, mod, handler;
 4   if (typeof depMap === 'string') {
 5     //Dependency needs to be converted to a depMap
 6     //and wired up to this module.
 7     depMap = makeModuleMap(depMap,
 8                                   (this.map.isDefine ? this.map : this.map.parentMap),
 9                                   false,
10                                   !this.skipMap);
11     this.depMaps[i] = depMap;
12     handler = getOwn(handlers, depMap.id);
13     if (handler) {
14       this.depExports[i] = handler(this);
15       return;
16     }
17     this.depCount += 1;
18 on(depMap, 'defined', bind(this, function (depExports) {
19   this.defineDep(i, depExports);
20   this.check();
21 }));
22     if (this.errback) {
23       on(depMap, 'error', bind(this, this.errback));
24     }
25   }
26   id = depMap.id;
27   mod = registry[id];
28   //Skip special modules like 'require', 'exports', 'module'
29   //Also, don't call enable if it is already enabled,
30   //important in circular dependency cases.
31   if (!hasProp(handlers, id) && mod && !mod.enabled) {
32     context.enable(depMap, this);
33   }
34 }));

這是很是關鍵的一段代碼,不管裏面的depcount仍是其中的on defined事件點註冊皆十分關鍵this

從這裏開始會加載util相關資源,因而util進入了相關加載流程了,這也是小卡關注的地方

可是這裏有一個不同的地方是,util模塊時具備parentModuleMap的,而common/lib不具備

這裏util與lib有一個映射關係lib->util,因此util的parentName就是common/lib

這個時候就到了解析URL這個步驟了

//name=>./util; parentName=>common/lib
normalizedName = normalize(name, parentName, applyMap);

咱們要作的事情就是解析這個地址

/**
         * Given a relative module name, like ./something, normalize it to
         * a real name that can be mapped to a path.
         * @param {String} name the relative name
         * @param {String} baseName a real name that the name arg is relative
         * to.
         * @param {Boolean} applyMap apply the map config to the value. Should
         * only be done if this normalization is for a dependency ID.
         * @returns {String} normalized name
         */
function normalize(name, baseName, applyMap) {
  var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, foundMap, foundI, foundStarMap, starI, normalizedBaseParts, baseParts = (baseName && baseName.split('/')),
  map = config.map,
  starMap = map && map['*'];

  //Adjust any relative paths.
  if (name) {
    name = name.split('/');
    lastIndex = name.length - 1;

    // If wanting node ID compatibility, strip .js from end
    // of IDs. Have to do this here, and not in nameToUrl
    // because node allows either .js or non .js to map
    // to same file.
    if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
      name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
    }

    // Starts with a '.' so need the baseName
    if (name[0].charAt(0) === '.' && baseParts) {
      //Convert baseName to array, and lop off the last part,
      //so that . matches that 'directory' and not name of the baseName's
      //module. For instance, baseName of 'one/two/three', maps to
      //'one/two/three.js', but we want the directory, 'one/two' for
      //this normalization.
      normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
      name = normalizedBaseParts.concat(name);
    }

    trimDots(name);
    name = name.join('/');
  }

  //Apply map config if available.
  if (applyMap && map && (baseParts || starMap)) {
    nameParts = name.split('/');

    outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
      nameSegment = nameParts.slice(0, i).join('/');

      if (baseParts) {
        //Find the longest baseName segment match in the config.
        //So, do joins on the biggest to smallest lengths of baseParts.
        for (j = baseParts.length; j > 0; j -= 1) {
          mapValue = getOwn(map, baseParts.slice(0, j).join('/'));

          //baseName segment has config, find if it has one for
          //this name.
          if (mapValue) {
            mapValue = getOwn(mapValue, nameSegment);
            if (mapValue) {
              //Match, update name to the new value.
              foundMap = mapValue;
              foundI = i;
              break outerLoop;
            }
          }
        }
      }

      //Check for a star map match, but just hold on to it,
      //if there is a shorter segment match later in a matching
      //config, then favor over this star map.
      if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
        foundStarMap = getOwn(starMap, nameSegment);
        starI = i;
      }
    }

    if (!foundMap && foundStarMap) {
      foundMap = foundStarMap;
      foundI = starI;
    }

    if (foundMap) {
      nameParts.splice(0, foundI, foundMap);
      name = nameParts.join('/');
    }
  }

  // If the name points to a package's name, use
  // the package main instead.
  pkgMain = getOwn(config.pkgs, name);

  return pkgMain ? pkgMain: name;
}
核心代碼

PS:我看requireJS版本,又老了,他的代碼又有更新啊!!!

上面這段代碼是一個關鍵

首先他會將common/lib的目錄解析出來,這裏是common('one/two/three.js', but we want the directory, 'one/two' )
咱們這裏首次就直接返回了,這裏返回的是 common/util
如果咱們換一個寫法,會一樣執行上面邏輯,最後卻有所不一樣,由於這個時候parent的common不見了!
這個時候便會執行返回util字符串,因此這裏兩個地址便會有所不一樣:
main.js=>require(['common/lib'], function (Lib)=>common/util
main.js=>require(['lib'], function (Lib)=>util
main.js=>require(['a/b/c/lib'], function (Lib)=>a/b/c/util

這裏util是相對於父級的目錄,這個是其地址變化的主要緣由

因此,如今關於小卡的問題應該獲得瞭解決,至於其map映射關係是如何造成的,這個話題就更加深了

小釵requireJS也是初學,不少不懂,不知是否是解決了小卡的問題,這裏提出來各位高手一塊兒看看,有誤請提出。

相關文章
相關標籤/搜索