【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也是初学,不少不懂,不知是否是解决了小卡的问题,这里提出来各位高手一块儿看看,有误请提出。

相关文章
相关标签/搜索