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

 
首先以其例子为例:
  1. requirejs.config({
  2. baseUrl: 'js'
  3. });
  4. // 依赖lib.js,实际加载的路径是 js/common/lib.js,而lib模块又依赖于util模块('./util'),解析后的实际路径为 js/common/util.js
  5. require(['common/lib'], function(Lib){
  6. Lib.say('hello');
  7. });
  1. // 依赖util模块
  2. define(['./util'], function(Util){
  3. return {
  4. say: function(msg){
  5. Util.say(msg);
  6. }
  7. };
  8. });

若是变个写法,util的目录结构就变了

  1. requirejs.config({
  2. baseUrl: 'js',
  3. paths: {
  4. lib: 'common/lib'
  5. }
  6. });
  7.  
  8. // 实际加载的路径是 js/common/lib.js
  9. require(['lib'], function(Lib){
  10. Lib.say('hello');
  11. });
  1. // util模块解析后的路径为 js/util.js
  2. define(['./util'], function(Lib){
  3. return {
  4. say: function(msg){
  5. Lib.say(msg);
  6. }
  7. };
  8. });

我们今天便一起来学习下这个问题

requireJS的basePath

对于baseUrl的解析需要注意,当满足以下条件,将不会相对baseUrl
① 以"/"开头
② 以".js"结尾
③ 包含各种协议
不出现以上条件,设置的path,是相对于baseUrl的

简单requireJS流程

然后我们这里再简单的整理下requireJS这部分的流程:
① 通过require加载主干流程,这里就是我们所谓的入口,以上述代码为例,入口是:
  1. require(['common/lib'], function(Lib){
  2. Lib.say('hello');
  3. });

该代码会在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这个步骤了

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

我们要做的事情就是解析这个地址

  1. /**
  2. * Given a relative module name, like ./something, normalize it to
  3. * a real name that can be mapped to a path.
  4. * @param {String} name the relative name
  5. * @param {String} baseName a real name that the name arg is relative
  6. * to.
  7. * @param {Boolean} applyMap apply the map config to the value. Should
  8. * only be done if this normalization is for a dependency ID.
  9. * @returns {String} normalized name
  10. */
  11. function normalize(name, baseName, applyMap) {
  12. var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, foundMap, foundI, foundStarMap, starI, normalizedBaseParts, baseParts = (baseName && baseName.split('/')),
  13. map = config.map,
  14. starMap = map && map['*'];
  15.  
  16. //Adjust any relative paths.
  17. if (name) {
  18. name = name.split('/');
  19. lastIndex = name.length - 1;
  20.  
  21. // If wanting node ID compatibility, strip .js from end
  22. // of IDs. Have to do this here, and not in nameToUrl
  23. // because node allows either .js or non .js to map
  24. // to same file.
  25. if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
  26. name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
  27. }
  28.  
  29. // Starts with a '.' so need the baseName
  30. if (name[0].charAt(0) === '.' && baseParts) {
  31. //Convert baseName to array, and lop off the last part,
  32. //so that . matches that 'directory' and not name of the baseName's
  33. //module. For instance, baseName of 'one/two/three', maps to
  34. //'one/two/three.js', but we want the directory, 'one/two' for
  35. //this normalization.
  36. normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
  37. name = normalizedBaseParts.concat(name);
  38. }
  39.  
  40. trimDots(name);
  41. name = name.join('/');
  42. }
  43.  
  44. //Apply map config if available.
  45. if (applyMap && map && (baseParts || starMap)) {
  46. nameParts = name.split('/');
  47.  
  48. outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
  49. nameSegment = nameParts.slice(0, i).join('/');
  50.  
  51. if (baseParts) {
  52. //Find the longest baseName segment match in the config.
  53. //So, do joins on the biggest to smallest lengths of baseParts.
  54. for (j = baseParts.length; j > 0; j -= 1) {
  55. mapValue = getOwn(map, baseParts.slice(0, j).join('/'));
  56.  
  57. //baseName segment has config, find if it has one for
  58. //this name.
  59. if (mapValue) {
  60. mapValue = getOwn(mapValue, nameSegment);
  61. if (mapValue) {
  62. //Match, update name to the new value.
  63. foundMap = mapValue;
  64. foundI = i;
  65. break outerLoop;
  66. }
  67. }
  68. }
  69. }
  70.  
  71. //Check for a star map match, but just hold on to it,
  72. //if there is a shorter segment match later in a matching
  73. //config, then favor over this star map.
  74. if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
  75. foundStarMap = getOwn(starMap, nameSegment);
  76. starI = i;
  77. }
  78. }
  79.  
  80. if (!foundMap && foundStarMap) {
  81. foundMap = foundStarMap;
  82. foundI = starI;
  83. }
  84.  
  85. if (foundMap) {
  86. nameParts.splice(0, foundI, foundMap);
  87. name = nameParts.join('/');
  88. }
  89. }
  90.  
  91. // If the name points to a package's name, use
  92. // the package main instead.
  93. pkgMain = getOwn(config.pkgs, name);
  94.  
  95. return pkgMain ? pkgMain: name;
  96. }

核心代码

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

上面这段代码是一个关键

首先他会将common/lib的目录解析出来,这里是common('one/two/three.js', but we want the directory, 'one/two' )
我们这里首次就直接返回了,这里返回的是common/util
若是我们换一个写法,会同样执行上面逻辑,最后却有所不同,因为这个时候parent的common不见了!
这个时候便会执行返回util字符串,所以这里两个地址便会有所不同:
  1. main.js=>require(['common/lib'], function (Lib)=>common/util
  2. main.js=>require(['lib'], function (Lib)=>util
  3. 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. 谈谈关键字strictfp

     Java语言中的其中一个设计目标是可移植性.无论在哪个虚拟机上运行,同一个计算应该得到同样的结果.对于浮点数的算术运算,实现这样的可移植性是相当困难的.double 类型使用 64 位存储一个 do ...

  2. OpenCASCADE Performance Test

    OpenCASCADE Performance Test eryar@163.com Abstract. Use the Draw Test Harness to test the performan ...

  3. Android使用TextureView播放视频

    1.引言 如果你想显示一段在线视频或者任意的数据流比如视频或者OpenGL 场景,你可以用android中的TextureView做到. 1).TextureView的兄弟SurfaceView 应用 ...

  4. Android随笔之——Activity中启动另一应用

    最近在写语音交互程序,在语音打开应用这块碰到如何用代码控制应用启动的问题.百度了一下,有两种方案:1.获取应用的包名:2.获取应用的包名.入口类名. 之前对两种方案都进行了尝试,发现方案二中存在一个弊 ...

  5. 基于讯飞语音API应用开发之——离线词典构建

    最近实习在做一个跟语音相关的项目,就在度娘上搜索了很多关于语音的API,顺藤摸瓜找到了科大讯飞,虽然度娘自家也有语音识别.语义理解这块,但感觉应该不是很好用,毕竟之前用过百度地图的API,有问题也找不 ...

  6. EntityFramework DbContext 线程安全

    先看这一段异常信息: A second operation started on this context before a previous asynchronous operation compl ...

  7. Flume NG Getting Started(Flume NG 新手入门指南)

    Flume NG Getting Started(Flume NG 新手入门指南)翻译 新手入门 Flume NG是什么? 有什么改变? 获得Flume NG 从源码构建 配置 flume-ng全局选 ...

  8. win7系统下如何配置php-Apache-mysql环境

    如何在win7系统下配置php环境呢,php+Apache+mysql都是在配置过程中必不可少的元素,php负责解析php代码,apache负责服务器端而mysql是数据交互的中转站. 那么如何将ph ...

  9. kafka源码分析之二客户端分析

    客户端由两种:生产者和消费者 1. 生产者 先看一下生产者的构造方法: private KafkaProducer(ProducerConfig config, Serializer<K> ...

  10. CSS浮动文摘

    很早以前就接触过CSS,但对于浮动始终非常迷惑,可能是自身理解能力差,也可能是没能遇到一篇通俗的教程.        写在前面的话:        由于CSS内容比较多,没有精力从头到尾讲一遍,只能有 ...