【requireJS路径加载】与程序员小卡的交流

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

http://www.cnblogs.com/chyingp/p/3677425.html

http://www.cnblogs.com/chyingp/p/requirejs-path-resolve.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的目录结构就变了


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的逻辑,即这段代码:


 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事件点注册皆十分关键

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

时间: 2024-10-06 21:30:03

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

展望未来,总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告【转载】

走过的路,回忆起来是那么曲折,把自己的一些心得体会分享给程序员兄弟姐妹们,虽然时代在变化,但是很可能你也会走我已经做过的10年的路程,有些心得体会你可以借鉴一下,觉得说得有道理的你就接纳,觉得说得没道理的,你就抛弃,以下是我发自内心的,给大家的忠告,特别是针对那些小弟弟妹妹们.01. 自己的户口档案.养老保险.医疗保险.住房公积金一定要保管好.由于程序员行业每年跳槽一次,我不隐瞒大家,我至少换过5个以上的单位,这期间跳来跳去,甚至是城市都换过3个.还好户口没丢掉,其他都已经是乱了,好几个城市里,

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

 内容简介 1.Synergy简介 2.Synergy安装与配置 3.附录 [程序员小助手]系列 在这个系列文章中(不定期更新),小编会把这些年(也没几年)的编程学习和工作中使用到的个人感觉非常好的软件推荐给大家,希望能够共享美好资源,使大家提高编程和办事效率. Synergy,感受穿越屏幕之美 什么?你面前有多个电脑屏幕,而你每个屏幕(或每台电脑)单独配一个鼠标和一个键盘? 什么?你在多个屏幕之间复制黏贴东西的时候还需要借助samba这样的共享软件? 什么?你只有一台电脑,只有一个屏幕?好吧

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

内容简介 1.Emacs简介 2.Emacs三个平台的安装与配置 3.自动补全插件 4.小编的Emacs配置文件 5.常用快捷方式 6.和版本控制系统的配合(以SVN为例) [程序员小助手]系列 在这个系列文章中(不定期更新),小编会把这些年(也没几年)的编程学习和工作中使用到的个人感觉非常好的软件推荐给大家,希望能够共享美好资源,使大家提高编程和办事效率. Emacs,最强编辑器,没有之一 小编知道,此标题一出,肯定会遭受广大群众“诟病”,说不好还会被其他编辑器的粉丝暗地里“干掉”. 比如,V

【转载分享】总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告

展望未来,总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告 走过的路,回忆起来是那么曲折,把自己的一些心得体会分享给程序员兄弟姐妹们,虽然时代在变化,但是很可能你也会走我已经做过的10年的路程,有些心得体会你可以借鉴一下,觉得说得有道理的你就接纳,觉得说得没道理的,你就抛弃,以下是我发自内心的,给大家的忠告,特别是针对那些小弟弟妹妹们. 01. 自己的户口档案.养老保险.医疗保险.住房公积金一定要保管好.由于程序员行业每年跳槽一次,我不隐瞒大家,我至少换过5个以上的单位,这期间

百度地图多点路径加载以及调整页面js

1 $(document).ready(function () { 2 /*用正则表达式获取url传递的地址参数,split后获得地址数组*/ 3 bmap = new BMap.Map('mapcontainer'); 4 var point = new BMap.Point(116.404, 39.915);//地图中心点 5 bmap.centerAndZoom(point, 15);//调整缩放以及设立中心点 6 bmap.enableScrollWheelZoom(); 7 var l

动态加载页面数据的小工具 javascript + jQuery (持续更新)

使用该控件,可以根据url,参数,加载html记录模板(包含json参数对应,以及具体记录位置Index根据参数描述加载对应的属性,并可以根据简单的判断分支加载对应html或者控件)至列表容器内(JQuery选择器字符串)注: 该控件在使用前需引入JQuery框架支持,使用该控件,可极大的减少Ajax列表数据动态加载开发工作的实际工作量. 使用方式: 首先,添加控件引用,并加入Jquery支持 <script src="js/jquery.js"></script&g

Oracle Data Pump不使用直接路径加载的场景

Situations in Which Direct Path Load Is Not Used If any of the following conditions exist for a table, then Data Pump uses external tables rather than direct path to load the data for that table: A global index on multipartition tables exists during

html中的图片、css、js等路径加载问题

网页文件的存取路径有3种:物理路径.绝对路径和相对路径. 物理路径就是你的文件放在主机上的具体位置,例如:D:\\image\\1.jpg 这种格式,该方法可以很快确定出你的文件,但是在网页显示路径基本很少用. 绝对路径是以网站的根目录(www)作为起始点,某文件在该项目中具体位置信息.比如你的网站放在 /var/www 中,那么 /var/www 就是你的根目录.如果文件 head.jpg 放在该目录的 image 里,那么 head.jpg 的路径信息就是 /image/head.jpg.但

servlet资源路径加载详解(3)

1.对于servlet的读取资源文件,要考虑工程文件路径和tomcat服务器的部署后的文件,所以一定要弄清楚加载文件路径位置,同时要考虑采用哪种方式: 一般有三种方式: (1).采用传统方式即jdk中提供的InputStream is = new FileInputStream("config.properties"); (2).采用ServletContext对象加载 (3).采用类加载器的方式 2.用代码检验 package cn.wwh.www.web.servlet; impo