这两天正好看到了程序员小卡同学的一篇博客,里面对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. 分区函数Partition By的与row_number()的用法以及与排序rank()的用法详解(获取分组(分区)中前几条记录)

    partition by关键字是分析性函数的一部分,它和聚合函数不同的地方在于它能返回一个分组中的多条记录,而聚合函数一般只有一条反映统计值的记录,partition by用于给结果集分组,如果没有指 ...

  2. 读书笔记-you-don't-konw-js

    第一部分:作用域和闭包 不要满足于只是让代码正常工作,而是弄清楚为什么是这样 作用域是什么 定义的变量存储在哪里?程序是如何找到变量的?实现的 规则就是作用域 传统编译语言执行前的编译三步骤(p5) ...

  3. 无法启动WP Emulator

    记得以前Vware不能运行设置的东西了吗?http://www.cnblogs.com/dunitian/p/4480750.html 如果不清楚可以参考上面的链接 重启的时候选择第二项 重新打开就o ...

  4. C#设计模式系列:原型模式(Prototype)

    1.原型模式简介 1.1>.定义 原型模式(Prototype)用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象. 1.2>.使用频率 中 1.3>.原型模式应用 首先 ...

  5. Use Qt in Debian for OpenCASCADE

    Use Qt in Debian for OpenCASCADE eryar@163.com Recently several OpenCASCADE enthusiasts want to buil ...

  6. lintcode 落单的数(位操作)

    题目1 落单的数 给出2*n + 1 个的数字,除其中一个数字之外其他每个数字均出现两次,找到这个数字. 链接:http://www.lintcode.com/zh-cn/problem/single ...

  7. DIV元素水平和垂直居中

    在前端开发过程中,经常要对元素进行居中设置.一般有水平居中,和垂直居中.一般设置水平居中简单.基本是margin:0 auto,就可以了.但是垂直居中,我们有时会觉得使用vertical-align, ...

  8. iOS开发之多线程技术(NSThread、OperationQueue、GCD)

    在前面的博客中如果用到了异步请求的话,也是用到的第三方的东西,没有正儿八经的用过iOS中多线程的东西.其实多线程的东西还是蛮重要的,如果对于之前学过操作系统的小伙伴来说,理解多线程的东西还是比较容易的 ...

  9. hdu4833 Best Financing(DP)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4833 这道题目关键的思想是从后往前dp,dp[i]表示在第i处投资xi能获得的最大收益,其中xi表示从 ...

  10. DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(1)

    好久没写 DDD 领域驱动设计相关的文章了,嘎嘎!!! 这几天在开发一个新的项目,虽然不是基于领域驱动设计的,但我想把 DDD 架构设计的一些东西运用在上面,但发现了很多问题,这些在之前的短消息项目中 ...