详细源代码一共就2000多行,来看我这篇分析的同学应该都下载下来了,好了,话不多说,开始:

代码的开头就出现3个全局变量: requirejs, require, define

  1. var requirejs, require, define;
  2.  
  3. (function(global, setTimeout){
  4.  
  5. balababla......
  6.  
  7. })(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout)))

  

require 和 define 大家应该都知道上干什么的,说实话,我是不知道的,在分析代码的时候,我从来也没用过这个框架,就听过AMD,就来直接看源码了。

如果你也不是很清楚,这2个变量是干什么的,我就来简单介绍一下,懂得的同学要是发现我说错了,希望指点我也一下。

主页面 index.html:

注意src是引入我们的requirejs库,  data-main:就是我们第一次用requrie的地方:

  1. <html>
  2. <head>
  3. <title></title>
  4. </head>
  5. <body>
  6. <script src="require.js" type="text/javascript" data-main="main.js"></script>
  7. </body>
  8. </html>

 main.js:

这里2个代码块都是依赖require的:

(1)require.config:配置

(2)requrie(); 加载需要的函数,注意里面的 ['name', 'say'],其实都是文件名,它们都在./js/ 目录下,具体看conifg

  1. require.config({
  2. baseUrl: '',
  3. paths: {
  4. 'nameDep': 'js/nameDep',
  5. 'say': 'js/say',
  6. 'name': 'js/name'
  7. },
  8. shim: {
  9. 'name': {
  10. deps: ['nameDep']
  11. }
  12. }
  13. });
  14. require(['name', 'say'], function (name, say) {
  15. say(name);
  16. });

  

./js/name.js  和 ./js/say.js

  1. //name
  2. define([''], function () {
  3. return '测试';
  4. });

  

  1. //say
  2. define([], function () {
  3. return function (name) {
  4. console.log(name);
  5. };
  6. });

  

最后注意在config中有个skim,这里面也是定义js文件的,只是由于他可能不符合AMD加载的规范

./js/nameDep.js

  1. console.log("nameDep.js")

  

以上分析,我们大概看得出  require 和 define 都是 function

--------------------------------------分割线-------------------------------------------------------------------------

下面进入2000多行匿名函数的讲解:

首先说一下2个参数: global 和 setTimeout

  1. (function (global, setTimeout) {
  2.  
  3. })(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout))

  

如果在浏览器的环境中,就是平时我们所知的  windows对象 和 windows.setTimeout方法 。本文只讨论浏览器环境,别的环境咱不考虑。

接下来就是一大堆的变量和函数的定义,太多了!反正我就扫了一眼,记不住?无所谓,只需要记住这开头这3个变量就行了:req,contexts,cfg

2个是空对象  cfg,contexts ,还有一个函数 req 。

为什么它是函数??? 你一拉到底,一定会看到 req = requirejs = function(){} 的定义,眼神不好也没关系,看我下面的代码框里有req({});

  1. var req,
  2. ....
  3. contexts = {},
  4. cfg = {},
  5. ....
  6. //各种function定义
  7. ....
  8.  
  9. //Create default context.
  10. req({});
  11.  
  12. .....

  

好了把眼神定位到 req({})吧, 因为这是上面一大堆定义后,第一次执行了代码!!!!

req这个函数长什么样子?? 你用文字匹配往上找:

  1. /**
  2. * Main entry point.
  3. *
  4. * If the only argument to require is a string, then the module that
  5. * is represented by that string is fetched for the appropriate context.
  6. *
  7. * If the first argument is an array, then it will be treated as an array
  8. * of dependency string names to fetch. An optional function callback can
  9. * be specified to execute when all of those dependencies are available.
  10. *
  11. * Make a local req variable to help Caja compliance (it assumes things
  12. * on a require that are not standardized), and to give a short
  13. * name for minification/local scope use.
  14. */
  15. req = requirejs = function (deps, callback, errback, optional) {
  16.  
  17. //Find the right context, use default
  18. var context, config,
  19. contextName = defContextName; //开头定义了 defContextName = "_"
  20.  
  21. // Determine if have config object in the call.
  22. if (!isArray(deps) && typeof deps !== 'string') {
  23. // deps is a config object
  24. config = deps;
  25. if (isArray(callback)) {
  26. // Adjust args if there are dependencies
  27. deps = callback;
  28. callback = errback;
  29. errback = optional;
  30. } else {
  31. deps = [];
  32. }
  33. }
  34.  
  35. //第一次没有config.context 跳过
  36. if (config && config.context) {
  37. contextName = config.context;
  38. }
  39.  
  40. //第一次context == undefined
  41. context = getOwn(contexts, contextName);
  42.  
  43. //第一次进入
  44. if (!context) {
  45.  
  46. //只有第一次会调用newContext("_")
  47. context = contexts[contextName] = req.s.newContext(contextName);
  48.  
  49. }
  50.  
  51. if (config) {
  52. context.configure(config);
  53. }
  54.  
  55. return context.require(deps, callback, errback);
  56. };

  

上面代码  context出现的频率非常高,说明这小子很重要。那我们来看看这小子到底是啥!!!

直接找到它的定义:

  1. context = contexts[contextName] = req.s.newContext(contextName);

  

那 newContext 又是什么鬼! 搜它!!!!!

  

  1. function newContext(contextName) {
  2.  
  3. var context
  4. ...
  5. //一大堆function定义
  6. ....
  7. context={
  8. .....
  9.  
  10. }
  11. context.require = context.makeRequire();
  12. return context;
  13. }

  

用20秒的时间扫一眼!老套路,一大堆的变量,函数的定义。 一直到最后return 一个 context变量。

再往上拉看看 context 到底是个什么鬼!!

记忆力没毛病的话,发现context对象里装的全是刚才定义过一些变量,函数。

-----------------------------------------------------------------------------------------------

|    其中return的上一行代码:                                                                   |

|              context.require = context.makeRequire();                                  |

|      有兴趣就去看一眼,很简单,就是返回一个叫localRequire的闭包     |

------------------------------------------------------------------------------------------------

OK,这个newContext函数我们已经差不多了解了,就是返回一个对象,通过这个对象控制newContext里的一系列私有变量和对象。

我们的contexts就如下图所示:

接下去走

context.configure(config);

我建议你去看一下,因为里面啥也没干,你只需要花10秒左右的时间就可以看完。。。

然后走

  1. return context.require(deps, callback, errback);

看上面的图,context.require 就是 localRequire

接下来,我们走进localRequire的世界里一探究竟!!!!其中deps = [ ]

  1. function localRequire(deps, callback, errback) {
  2. var id, map, requireMod;
  3.  
  4. if (options.enableBuildCallback && callback && isFunction(callback)) {
  5. callback.__requireJsBuild = true;
  6. }
  7.  
  8. if (typeof deps === 'string') {
  9. if (isFunction(callback)) {
  10. //Invalid call
  11. return onError(makeError('requireargs', 'Invalid require call'), errback);
  12. }
  13.  
  14. //If require|exports|module are requested, get the
  15. //value for them from the special handlers. Caveat:
  16. //this only works while module is being defined.
  17. if (relMap && hasProp(handlers, deps)) {
  18. return handlers[deps](registry[relMap.id]);
  19. }
  20.  
  21. //Synchronous access to one module. If require.get is
  22. //available (as in the Node adapter), prefer that.
  23. if (req.get) {
  24. return req.get(context, deps, relMap, localRequire);
  25. }
  26.  
  27. //Normalize module name, if it contains . or ..
  28. map = makeModuleMap(deps, relMap, false, true);
  29. id = map.id;
  30.  
  31. if (!hasProp(defined, id)) {
  32. return onError(makeError('notloaded', 'Module name "' +
  33. id +
  34. '" has not been loaded yet for context: ' +
  35. contextName +
  36. (relMap ? '' : '. Use require([])')));
  37. }
  38. return defined[id];
  39. }
  40.  
  41. //Grab defines waiting in the global queue.
  42. intakeDefines();
  43.  
  44. //Mark all the dependencies as needing to be loaded.
  45. context.nextTick(function () {
  46. //Some defines could have been added since the
  47. //require call, collect them.
  48. intakeDefines();
  49.  
  50. requireMod = getModule(makeModuleMap(null, relMap));
  51.  
  52. //Store if map config should be applied to this require
  53. //call for dependencies.
  54. requireMod.skipMap = options.skipMap;
  55.  
  56. requireMod.init(deps, callback, errback, {
  57. enabled: true
  58. });
  59.  
  60. checkLoaded();
  61. });
  62.  
  63. return localRequire;
  64. }

  

很快我们就把目光锁定到(因为,前面的一大段代码都是if语句,不符合条件进不去)

  1. //Grab defines waiting in the global queue.
  2. intakeDefines();

  

intakeDefines --》takeGlobalQueue--》context.nextTick--》intakeDefines --》  requireMod.init --》  checkLoaded();

里面大致就是上面所示,因为有点复杂,我这里暂时先不说,接续走简单的。。

到此为止,我们已经走完了一边req。

大致流程如下图所示:

接下去,我们再一拉到底!!!!

倒数第二行:

  1. //Set up with config info.
  2. req(cfg);

 

cfg还记得是什么吗?? 一开始是定义为空对象啊!!!

有地方修改过它吗? 往上拉一拉!!

  1. //Look for a data-main script attribute, which could also adjust the baseUrl.
  2. if (isBrowser && !cfg.skipDataMain) {
  3. //Figure out baseUrl. Get it from the script tag with require.js in it.
  4. eachReverse(scripts(), function(script) {
  5. //Set the 'head' where we can append children by
  6. //using the script's parent.
  7. if (!head) {
  8. head = script.parentNode;
  9. }
  10.  
  11. //Look for a data-main attribute to set main script for the page
  12. //to load. If it is there, the path to data main becomes the
  13. //baseUrl, if it is not already set.
  14. dataMain = script.getAttribute('data-main');
  15. if (dataMain) {
  16. //Preserve dataMain in case it is a path (i.e. contains '?')
  17. mainScript = dataMain;
  18.  
  19. //Set final baseUrl if there is not already an explicit one,
  20. //but only do so if the data-main value is not a loader plugin
  21. //module ID.
  22. if (!cfg.baseUrl && mainScript.indexOf('!') === -1) {
  23.  
  24. //Pull off the directory of data-main for use as the
  25. //baseUrl.
  26. src = mainScript.split('/');
  27.  
  28. mainScript = src.pop();
  29.  
  30. subPath = src.length ? src.join('/') + '/' : './';
  31.  
  32. cfg.baseUrl = subPath;
  33. }
  34.  
  35. //Strip off any trailing .js since mainScript is now
  36. //like a module name.
  37. mainScript = mainScript.replace(jsSuffixRegExp, '');
  38.  
  39. //If mainScript is still a path, fall back to dataMain
  40. if (req.jsExtRegExp.test(mainScript)) {
  41.  
  42. mainScript = dataMain;
  43. }
  44.  
  45. //Put the data-main script in the files to load.
  46. cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
  47.  
  48. return true;
  49. }
  50. });
  51. }

  

这里面做的事情很简单,就是找到页面中包含 "data-main" 属性的script标签,分析路径。

最后得到这样的结果:

好了。再回到req(cfg);

这一次我们好好的走一走上面流程图里的步骤。。。。

把目光迅速锁定到:

  1. return context.require(deps, callback, errback);

  

 

一步一步带你分析 requirejs的更多相关文章

  1. 在net中json序列化与反序列化 面向对象六大原则 (第一篇) 一步一步带你了解linq to Object 10分钟浅谈泛型协变与逆变

    在net中json序列化与反序列化   准备好饮料,我们一起来玩玩JSON,什么是Json:一种数据表示形式,JSON:JavaScript Object Notation对象表示法 Json语法规则 ...

  2. 一步一步带你实现virtual dom(二) -- Props和事件

    很高兴我们可以继续分享编写虚拟DOM的知识.这次我们要讲解的是产品级的内容,其中包括:设置和DOM一致性.以及事件的处理. 使用Babel 在继续之前,我们需要弥补前一篇文章中没有详细讲解的内容.假设 ...

  3. 一步一步带你实现virtual dom(一)

    一步一步带你实现virtual dom(一) 一步一步带你实现virtual dom(二)--Props和事件 要写你自己的虚拟DOM,有两件事你必须知道.你甚至都不用翻看React的源代码,或者其他 ...

  4. requirejs源码分析: requirejs 方法–2. context.require(deps, callback, errback);

    上一篇 requirejs源码分析: requirejs 方法–1. 主入口  中的return context.require(deps, callback, errback);  调用的是make ...

  5. 一步一步带你入门MySQL中的索引和锁 (转)

    出处: 一步一步带你入门MySQL中的索引和锁 索引 索引常见的几种类型 索引常见的类型有哈希索引,有序数组索引,二叉树索引,跳表等等.本文主要探讨 MySQL 的默认存储引擎 InnoDB 的索引结 ...

  6. requirejs源码分析: requirejs 方法–1. 主入口

    该方法是 主要的入口点 也是最常用的方法. req = requirejs = function (deps, callback, errback, optional) { //Find the ri ...

  7. (第一篇) 一步一步带你了解linq to Object

    要想学好linq to object 我们必须要先学习lambda 表达式,学习lambda 表达式呢我们必须了解匿名函数和匿名类及扩展方法,学习匿名函数,我们必须学会委托,这是本文的宗旨.下面开始第 ...

  8. 一步一步带你做WebApi迁移ASP.NET Core2.0

    随着ASP.NET Core 2.0发布之后,原先运行在Windows IIS中的ASP.NET WebApi站点,就可以跨平台运行在Linux中.我们有必要先说一下ASP.NET Core. ASP ...

  9. 一步一步带你安装史上最难安装的 vim 插件 —— YouCompleteMe

    YouCompleteMe is a fast, as-you-type, fuzzy-search code completion engine for Vim.参考: https://github ...

随机推荐

  1. JavaSE入门学习21:Java面向对象之接口(interface)(二)

    一接口实现的多态 在上一篇博文:JavaSE入门学习20:Java面向对象之接口(interface)(一)中提到了接口的实现存在多态性,那么 这一篇主要就要分析接口实现的多态. 实例一 Test.j ...

  2. 关于Linux网络配置

    Linux网络配置 一:什么是网络接口卡以及如何查看网络接口的网络信息:在Linux系统中,主机的网络接口卡通常称为“网络接口”,我们可以使用ifconfig命令来查看网络 接口的信息(普通用户使用/ ...

  3. jquery的push()

    JavaScript push() 方法 JavaScript Array 对象 定义和用法 push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度. 语法 arrayObject.pus ...

  4. 判断终端是ios还是安卓的一些妙用

    最近遇到一个项目 要求有两个icon(就是下载地址 下载安卓的apk  和ios的安装包) 一开始的方案是 什么设备都显示这两个icon 但是后来老大说这样不好   安卓用户给他下载ios 也不行  ...

  5. <button>标签也能提交表单问题

    如何避免<button>标签也能提交表单的问题: 只需加上一个属性:type='button'即可:如<button type="button"> < ...

  6. PHP自动加载功能原理解析

    前言 这篇文章是对PHP自动加载功能的一个总结,内容涉及PHP的自动加载功能.PHP的命名空间.PHP的PSR0与PSR4标准等内容. 一.PHP自动加载功能 PHP自动加载功能的由来 在PHP开发过 ...

  7. EasyDSS流媒体解决方案实现的实时数据统计报表、视频文件上传、点播、分享、集成代码等功能

    之前的EasyDSS作为rtmp流媒体服务器自从推出就备受用户好评,随着用户的需求的变更产品自身的发展是必须的: 为了更好的用户体验和和功能的完善,我们在EasyDSS的基础上增添了服务器硬件数据报表 ...

  8. Netty聊天室(2):从0开始实战100w级流量应用

    目录 客户端 Client 登录和响应处理 写在前面 客户端的会话管理 客户端的逻辑构成 连接服务器与Session 的创建 Session和 channel 相互绑定 AttributeMap接口的 ...

  9. thinkphp将APP_DEBUG常量设置为false后报错的问题

    ThinkPHP 将 APP_DEBUG 常量设置为 false 后出现了下面的问题: Parse error: syntax error, unexpected T_STRING in \www\R ...

  10. Bootstrap 轮播图(Carousel)插件

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...