前两天跟着叶小钗的博客,看了下RequireJS的源码,大体了解了其中的执行过程。不过在何时进行依赖项的加载,以及具体的代码在何处执行,还没有搞透彻,奈何能力不够,只能先记录一下了。

RequireJS的初探

看源码从头开始看,肯定是不切实际的。按照叶小钗的方法,是从data-main开始的,所以我们也从那里开始把!

首先,页面会有一段js标签,会去加载requirejs:

  1. <script data-main="test.js" src="lib/require.js"></script>

Requirejs中,代码是一个自执行的方法:

  1. var requirejs,require,define;
  2. (function(global){
  3. })(this);

源码中,主要是定义了三个全局的变量——requirejs,require,define,下面是一个自执行的方法。

那么主要就是看看这个方法里面都干了什么吧!

RequireJS主体方法

  1. //定义环境变量
  2. //定义各种方法
  3. //检查requirejs,require,define
  4. //核心部分
  5. function newContext(){}//定义核心部分方法
  6. req = requirejs = function(){//定义req
  7. //...
  8. return context.require();
  9. };
  10. req.config = function(){};
  11. req({});//创建默认的上下文
  12. req.createNode = function (config, moduleName, url) {
  13. var node = config.xhtml ?
  14. document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
  15. document.createElement('script');
  16. node.type = config.scriptType || 'text/javascript';
  17. node.charset = 'utf-8';
  18. node.async = true;
  19. return node;
  20. };
  21. //洋洋洒洒,加载代码
  22. req.load = function(){
  23. node = req.createNode(config, moduleName, url);//创建节点
  24. node.addEventListener('load', context.onScriptLoad, false);//添加load事件
  25. if (baseElement) {//插入到head里面
  26. head.insertBefore(node, baseElement);
  27. } else {
  28. head.appendChild(node);
  29. }
  30. };
  31. if (isBrowser && !cfg.skipDataMain) {
  32. //加载main.js
  33. }
  34. define = function(){};
  35. req.exec =function(){};
  36. req(cfg);//执行配置文件

上面的代码中,关键的方法定义其实只有两个:

  • 定义了newContext()方法,用于配置上下文环境,并且仅会执行一次!后续都是使用同一个context!
  • 定义req,它是后续使用的方法!

然后在上面的代码中,它做了下面三件事:

  • 1 执行req({}),传入了空的对象,初始化context
  • 2 if(isBrowser && xxxx)....,加载data-main所指向的js,读取配置
  • 3 执行req(cfg),执行刚刚读取的配置,加载目标模块...

基本上就是这个套路了!

newContext()

RequireJS最精彩的部分,就在这个方法里面了!

  1. function newContext(contextName){
  2. function getModule(depMap) {
  3. var id = depMap.id,
  4. mod = getOwn(registry, id);
  5. if (!mod) {
  6. mod = registry[id] = new context.Module(depMap);
  7. }
  8. return mod;
  9. }
  10. function checkLoaded() {
  11. }
  12. context = {
  13. //...
  14. makeRequire: function (relMap, options) {
  15. //核心
  16. function localRequire(deps, callback, errback) {
  17. //真正的核心
  18. context.nextTick(function () {
  19. intakeDefines();
  20. requireMod = getModule(makeModuleMap(null, relMap));//主要看这里吧
  21. requireMod.skipMap = options.skipMap;
  22. requireMod.init(deps, callback, errback, {
  23. enabled: true
  24. });
  25. checkLoaded();
  26. });
  27. }
  28. return localRequire;
  29. },
  30. load: function (id, url) {
  31. req.load(context, id, url);
  32. },
  33. onScriptLoad : function() {
  34. context.completeLoad();
  35. },
  36. completeLoad : function() {
  37. takeGlobalQueue();
  38. while(defQueue.length){
  39. }
  40. mod = getOwn(registry, moduleName);
  41. checkLoaded();
  42. }
  43. //...
  44. }
  45. Module.prototype = {
  46. init : function(depMaps, factory, errback, options){
  47. if (options.enabled || this.enabled) {
  48. this.enable();
  49. } else {
  50. this.check();
  51. }
  52. },
  53. fetch : function(){
  54. if (this.shim) {//依赖
  55. context.makeRequire(this.map, {
  56. enableBuildCallback: true
  57. })(this.shim.deps || [], bind(this, function () {
  58. return map.prefix ? this.callPlugin() : this.load();
  59. }));
  60. } else {
  61. //Regular dependency.
  62. return map.prefix ? this.callPlugin() : this.load();//是否包含前缀 text!xxx
  63. }
  64. },
  65. load: function () {
  66. var url = this.map.url;
  67. //Regular dependency.
  68. if (!urlFetched[url]) {
  69. urlFetched[url] = true;
  70. context.load(this.map.id, url);
  71. }
  72. },
  73. check : function(){
  74. this.fetch();
  75. },
  76. enable : function(){
  77. this.check();
  78. }
  79. };
  80. context.require = context.makeRequire();//其实是把localRequire赋值给context.require
  81. return context;
  82. };

这个newContext()里面定义大量的加载模块、校验、检查等工作。可以看到这个方法,主要是定义了一个context对象和Module方法。

然后执行这个方法后,会自动调用context对象的makeRequire()方法,这个makeRequire实际上调用的又是内部定义的localRequire()。LocalRequire则是处理加载任务的核心——比如依赖的检查,模块的加载等等。

执行点

  1. req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
  2. setTimeout(fn, 4);
  3. } : function (fn) { fn(); };

所有的加载都会交由这个nextTick执行,暂时没有搞清楚...

流程图

收获

  • 1 原来RequireJS加载模块的时候,是检查data-main属性,然后去加载目标js。
  • 2 加载到目标模块后,会按照它的依赖关系,进行加载,并且每个模块仅会加载一次。
  • 3 加载模块的时候,会绑定一个load事件,当加载完会触发事件,执行该js
  • 4 脚本实际上是通过创建了页面的script元素,然后添加到head里面。

RequireJS源码初探的更多相关文章

  1. Catalyst揭秘 Day2 Catalyst源码初探

    Catalyst揭秘 Day2 Catalyst源码初探 这节课从源码角度来讲catalyst. 首先有一个观点要澄清,就是技术不是越底层就越是性能和效率更高.因为除了指令执行性能以外,更重要的是架构 ...

  2. Servlet源码初探

    年底,公司的事情告一段落,就来捣鼓一下这个Servlet源码,为下一步的spingmvc源码初探做准备 1.Servlet接口 public interface Servlet { void init ...

  3. requireJS 源码(二) data-main 的加载实现

    (一)requireJs 的整体结构: requireJS 源码 前192行,是一些 变量的声明,工具函数的实现 以及 对 三个全局变量(requirejs,require,define)若被占用后的 ...

  4. 从 RequireJs 源码剖析脚本加载原理

    引言 俗话说的好,不喜欢研究原理的程序员不是好的程序员,不喜欢读源码的程序员不是好的 jser.这两天看到了有关前端模块化的问题,才发现 JavaScript 社区为了前端工程化真是煞费苦心.今天研究 ...

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

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

  6. RequireJs 源码解读及思考

    写在前面: 最近做的一个项目,用的require和backbone,对两者的使用已经很熟悉了,但是一直都有好奇他们怎么实现的,一直寻思着读读源码.现在项目结束,终于有机会好好研究一下. 本文重要解读r ...

  7. 【一起学源码-微服务】Feign 源码一:源码初探,通过Demo Debug Feign源码

    前言 前情回顾 上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在Dynam ...

  8. 【requireJS源码学习01】了解整个requireJS的结构

    前言 现在工作中基本离不开requireJS这种模块管理工具了,之前一直在用,但是对其原理不甚熟悉,整两天我们来试着学习其源码,而后在探寻其背后的AMD思想吧 于是今天的目标是熟悉requireJS整 ...

  9. 【requireJS源码学习02】data-main加载的实现

    前言 经过昨天的学习,我们大概了解到了requireJS的主要结构,这里先大概的回顾一下 首先从总体结构来说,require这里分为三块: ① newContext之前变量声明或者一些工具函数 ② n ...

随机推荐

  1. scala学习:apply方法

    摘抄两段话: 在明确了方法调用的接收者的情况下,若方法只有一个参数时,调用的时候就可以省略点及括号.如 " to ",实际完整调用是 ".to()".但 &qu ...

  2. 同一web系统,不同端口的跨域问题

    部署web系统的时候,发现了跨域问题,子系统是用Iframe嵌入到系统里面的,导致父窗口获取子系统的webService和图片时发现跨域问题,如下图所示:问题1:父窗口获取子系统的对象跨域 原因:用这 ...

  3. C#数据库操作

    1.常用的T-Sql语句      查询:SELECT * FROM tb_test WHERE ID='1' AND name='xia'                SELECT * FROM ...

  4. SQL语句-批量插入表(表数据插表)

    批量插入表(表数据插表) ****1.INSERT INTO SELECT语句语句形式为:Insert into Table2(field1,field2,...) select value1,val ...

  5. 交换机和VLAN

    交换机 交换机的两个作用:一是维护CAM表,CAM表是计算机的MAC地址和交换机端口的映射表:另一个是根据CAM来进行数据的转发. 以太网交换机转发数据帧有三种方式: 1.存储转发:即先存储后转发的方 ...

  6. fragment 碎片整理

    activity_m1.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xm ...

  7. [转载] 散列表(Hash Table)从理论到实用(上)

    转载自:白话算法(6) 散列表(Hash Table)从理论到实用(上) 处理实际问题的一般数学方法是,首先提炼出问题的本质元素,然后把它看作一个比现实无限宽广的可能性系统,这个系统中的实质关系可以通 ...

  8. POJ 3181 Dollar Dayz DP

    f[i][j]=f[i-j][j]+f[i][j-1],结果很大需要高精度. //#pragma comment(linker, "/STACK:1024000000,1024000000& ...

  9. Top Data Scientists to Follow & Best Data Science Tutorials on GitHub

    http://www.analyticsvidhya.com/blog/2015/07/github-special-data-scientists-to-follow-best-tutorials/ ...

  10. html css 注释

    html <!--       注释内容 --> css注释/*   注释内容*/