这两天正好看到了程序员小卡同学的一篇博客,里面对requireJS路径的解析做了一些说明,里面有点问题待解决,我这里正好知道一点,所以整理成文,不知对小卡同学是否有帮助。

 
首先以其例子为例:
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的目录结构就变了

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',他的键值便是这个了

这里会首先加载器依赖项,common/lib,而此时便会做第一步的解析并且形成一个模块

在模块加载时,会创建一个script标签,并且为其绑定load事件,这里会有第二个事件的触发

② 在加载common/lib模块时,有一个关键点需要注意:

  • 文件加载结束便会马上执行,所以其define方法执行了,并且往globalDefQueue写入了数据
  • load事件触发,会创建一个requireJS module,这个时候其依赖项会加载

上述虽然与本次讨论的东西无关,却是理解整个require的关键,各位可以去看看

③ context.completeLoad(data.id) =>但是这个时候却发现其有一个依赖项,于是便会先加载器依赖项,这里又会进入,main.js中require的逻辑,即这段代码:

 //Enable each dependency
each(this.depMaps, bind(this, function (depMap, i) {
var id, mod, handler;
if (typeof depMap === 'string') {
//Dependency needs to be converted to a depMap
//and wired up to this module.
depMap = makeModuleMap(depMap,
(this.map.isDefine ? this.map : this.map.parentMap),
false,
!this.skipMap);
this.depMaps[i] = depMap;
handler = getOwn(handlers, depMap.id);
if (handler) {
this.depExports[i] = handler(this);
return;
}
this.depCount += 1;
on(depMap, 'defined', bind(this, function (depExports) {
this.defineDep(i, depExports);
this.check();
}));
if (this.errback) {
on(depMap, 'error', bind(this, this.errback));
}
}
id = depMap.id;
mod = registry[id];
//Skip special modules like 'require', 'exports', 'module'
//Also, don't call enable if it is already enabled,
//important in circular dependency cases.
if (!hasProp(handlers, id) && mod && !mod.enabled) {
context.enable(depMap, this);
}
}));

这是非常关键的一段代码,无论里面的depcount还是其中的on defined事件点注册皆十分关键

从这里开始会加载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也是初学,很多不懂,不知是不是解决了小卡的问题,这里提出来各位高手一起看看,有误请提出。

【requireJS路径加载】与程序员小卡的交流的更多相关文章

  1. [转]微信小程序之加载更多(分页加载)实例 —— 微信小程序实战系列(2)

    本文转自;http://blog.csdn.net/michael_ouyang/article/details/56846185 loadmore 加载更多(分页加载) 当用户打开一个页面时,假设后 ...

  2. 【requireJS源码学习03】细究requireJS的加载流程

    前言 这个星期折腾了一周,中间没有什么时间学习,周末又干了些其它事情,这个时候正好有时间,我们一起来继续学习requireJS吧 还是那句话,小钗觉得requireJS本身还是有点难度的,估计完全吸收 ...

  3. 【程序员小助手】Synergy,感受穿越屏幕之美

    内容简介 1.Synergy简介 2.Synergy安装与配置 3.附录 [程序员小助手]系列 在这个系列文章中(不定期更新),小编会把这些年(也没几年)的编程学习和工作中使用到的个人感觉非常好的软件 ...

  4. 【程序员小助手】Emacs,最强编辑器,没有之一

    内容简介 1.Emacs简介 2.Emacs三个平台的安装与配置 3.自动补全插件 4.小编的Emacs配置文件 5.常用快捷方式 6.和版本控制系统的配合(以SVN为例) [程序员小助手]系列 在这 ...

  5. AngularJS + ui-router + RequireJS异步加载注册controller/directive/filter/service

    一般情况下我们会将项目所用到的controller/directive/filter/sercive预先加载完再初始化AngularJS模块,但是当项目比较复杂的情况下,应该是打开对应的界面才加载对应 ...

  6. Oracle直接路径加载--append的深度解析

    ㈠ 直接路径加载和buffer cache              直接路径插入的数据不经过buffer cache,从PGA直接把数据格式化成Oracle块       然后由普通的Oracle ...

  7. 程序员小张的第一篇博文 --记Markdown的使用学习

    1.前言 为了即将到来的面试做准备,以及记录一下平日里自己的学习过程和生活日常,我开始进驻博客园啦!这就是我的第一篇博客(有点小激动)~ 作为一只新手,首先记录一下今晚的编写博文的学习过程吧~ 2.使 ...

  8. jQuery自动加载更多程序

    1.1.1 摘要 现在,我们经常使用的微博.微信或其他应用都有异步加载功能,简而言之,就是我们在刷微博或微信时,移动到界面的顶端或低端后程序通过异步的方式进行加载数据,这种方式加快了数据的加载速度,由 ...

  9. 用UBOOT自带loadb命令加载应用程序到SDRAM中运行的方法

    S3C44B0开发板中,用UBOOT自带loadb命令加载应用程序到SDRAM中运行的方法    1.开发板说明:  开发板上已有移植好的UBOOT运行.   2.交叉编译工具链为arm-linu-g ...

随机推荐

  1. java中集合类中Collection接口中的Map接口的常用方法熟悉

    1:Map接口提供了将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值.Map接口中同样提供了集合的常用方法. 2:由于Map集合中的元素是通过key,value,进行存储的,要 ...

  2. js高程读书笔记(第4章--变量、作用域和内存)

    JavaScript变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已.由于不存在定义某个变量必须要保存何总数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变. 1. ...

  3. XenServer pool 移除server 设置master

    如果因为Pool中Master主机由于某种原因导致失效,会引起整个Pool进入紧急模式,恢复步骤如下: 在成员服务器上输入如下命令 # xe host-emergency-ha-disable     ...

  4. 如何部署Icinga服务端

    Icinga是Nagios的一个变种,配置,使用方式几乎一样,而且完全兼容Nagios的插件.所以下面的部署方案对Nagios同样使用. 它还推出了两个中文版本,icinga-cn原版和icinga- ...

  5. php使用post动态选择头像和js事件动态改变头像

    <html> <head> <meta http-equit="Content-type" content="text/html" ...

  6. ((uchar*)(Img1->imageData + Img1->widthStep*pt.y))[pt.x] 的 具体含义

    widthstep是指图像每行所占的字节数. 主要要和width区别: width是表示图像的每行像素数,widthStep指表示存储一行像素需要的字节数. 在OpenCV里边,widthStep必须 ...

  7. IE6下的png透明图片的背景定位

    在IE6下PNG透明图片做背景,无法使用background-position进行定位.但是可以使用margin和绝对定位来进行. 另外,由于IE6下的 :hover 只对<a>支持,对其 ...

  8. [ES] 基础概念

    Elasticsearch是基于Luence实现的一款搜索引擎,支持分布式和集群,并且搜索近实时,主要用于搜索和数据分析 索引 index 可以理解为数据库中的database,存储的是实际数据,因为 ...

  9. Oracle数据库资源管理

    1.了解Resource Manager术语 2.了解Resource Manager分配方法 3.了解DEFAULT_PLAN 4.新建资源计划 5.创建使用者组 6.了解资源分配方法 7.分配使用 ...

  10. Node.js 爬虫初探

    前言 在学习慕课网视频和Cnode新手入门接触到爬虫,说是爬虫初探,其实并没有用到爬虫相关第三方类库,主要用了node.js基础模块http.网页分析工具cherrio. 使用http直接获取url路 ...