require.js
RequireJS 是一个 JavaScript 文件和模块载入工具,针对浏览器使用场景优化,块作用域来申明function防止污染全局变量,声明不同js文件之间的依赖,按需、并行、延时载入js库,可应用到其他 JavaScript 环境中如 Rhino 和 Node.js
官网 //requirejs.org/
RequireJS是模块化加载js文件的框架
基于AMD规范实现 方便 js文件的异步加载


引入require.js的正确姿势
<script src="https://cdn.bootcss.com/require.js/2.3.5/require.min.js"></script>
<script>window.require||document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js"><\/script>')</script>

RequireJS采用的异步加载 前一个js文件没加载完毕,不会影响后一个js文件的加载
RequireJS加载的js文件 在独立的命名空间 js文件出现相同的变量不会产生冲突
RequireJS模块化的js让代码重用能力得到提高 团队协作开发更便利等


index.html文件
<!DOCTYPE html>
<head><meta charset="UTF-8"><title>Test</title></head>
<body>
<h1>Hello World!</h1>
<script src="https://cdn.bootcss.com/require.js/2.3.5/require.min.js"></script>
<script>window.require||document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js"><\/script>')</script>
<script type="text/javascript">
require(['app/a'], function(obj) {document.write('name: ' + obj.name + ', age: ' + obj.age);});
</script></body></html>
1、js目录存放的是所有的js文件
2、js/app目录存放的 自定义的模块js文件
3、js/lib目录存放的  js框架文件如jquery、react等
4、require.min.js是RequireJS框架的主文件
5、main.js是当require框架文件加载完毕后,首先执行的 文件 类似于C语言中的main函数
6、index.html是使用RequireJS模块化加载js文件的网页
配置RequireJS的参数
main.js的代码所示
require.config({
    baseUrl: 'js/lib',
    paths: {
        app: '../app'
    }
})
使用require.config()方法为requirejs框架配置一些属性,如上面代码中配置的baseUrl和paths。其中:
1、baseUrl属性指定了一个默认的路径,配置了该属性后,requirejs框架加载的所有js文件,都是以这个路径为基准的
2、paths属性指定了一系列的键值对,可以很方便地为一些较长的文件路径设置一个短的别名,例如下面的代码:
paths : {
    "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery"]   
}
即用jquery代表了http://libs.baidu.com/jquery/2.0.3/jquery路径。
使用RequireJS加载模块
需要加载jquery时 require(['jquery'], function($) {    });
需要加载自定义的a.js模块时 main.js中 paths里定义了一个app 值为‘../app’
表示使用app代替路径’../app’,注意这里的‘../app’路径是相对于baseUrl中指定的路径的
可以用如下代码来加载a.js模块:

require(['app/a'], function() {
    //这里的'app/a'就相当于加载了'js/app/a.js'文件,
    这里的'app/a'是不带文件后缀.js的,而且也不允许带后缀,否则无法正确加载js文件
});

require(['app/a'], function() {});
表示加载’app/a’这个模块,require()方法的第一个参数必须是一个数组
哪怕仅仅只加载一个模块;第二个参数是一个回调函数
当模块成功加载后得到执行

如何编写自定义的模块
编写自定义的模块时,也需要遵循AMD规范
1、自定义一个对象:
a.js文件
define({name: 'zhangsan',age: 18});
定义可被RequireJS加载的模块,需要使用define()方法,定义对象模块,直接在define()方法中传入这个对象即可,在加载并使用这个对象时
require(['app/a'], function(obj) {    document.write('name: ' + obj.name + ', age: ' + obj.age); });
回调函数中的obj参数就代表了在define()中传入的对象,可以直接使用obj.name和obj.age来访问这个对象的属性
2、自定义一个没有任何依赖的方法:
a.js文件代码
define(function() {
    var name = 'zhangsan', age = 20, sayHello = function(name, age) { document.write('hello, my name is: ' + name + ', my age is: ' + age);  };
    sayHello(name, age);
})

要定义一个函数模块,只需在define()的参数中传入一个函数function即可
在使用这个模块时
require(['app/a'], function() {});
加载完毕a模块后,执行a.js中定义的函数,打开浏览器后结果

3、自定义一个有依赖的方法:
a.js文件
define(['jquery'], function($) {   $('h1').css('color', 'red'); });
在main.js中的paths中,加入如下配置:
"jquery": ["http://libs.baidu.com/jquery/2.0.3/jquery"]
然后在index.html文件中加载a模块
require(['app/a'], function() {});
a.js文件中的define()方法带有两个参数,第一个参数为一个数组,表示这个模块依赖的其他模块,
由于在main.js配置文件中配置了jquery,所以a模块中依赖的jquery会被加载,加载完毕后,
在define()方法的第二个参数,也就是回调函数中,使用加载好的jquery,用$获取h1标签,然后将h1标签变成红色。
4、将一个方法作为模块:
a.js文件
define(function() {   return function(a, b) {   return a + b;   } });
define()方法中传入了一个function,该function的返回值为一个function,
传入两个参数a,b,然后返回a + b的值,在index.html文件中加载a模块
require(['app/a'], function(add) {    document.write('3 + 2 = ' + add(3, 2));});


5、简单包装CommonJS来定义模块
a.js文件
define(function(require, exports, modules) {
    var a = require('a'),b = require('b');  //Return the module value
    return function () {};
});

6、定义一个命名模块:
a.js文件
define("app/test", [], function() {});
define()中有3个参数,分别代表模块名、依赖的模块、回调函数,该方式不推荐使用,因为属于硬编码的模块,不利于模块的扩展和复用



依赖不使用requirejs方式的库

如果没用define(...) 定义模块  hello.js 通方式定义了一个函数
function hello() {
  alert("hello, world~");
}

用shim 将某个依赖中的某个全局变量暴露给requirejs当作这个模块本身的引用
requirejs.config({
  baseUrl: '/public/js',
  paths: {
    hello: 'hello'
  },
  shim: {
    hello: { exports: 'hello' }
  }
});
requirejs(['hello'], function(hello) {
  hello();
});

exports: 'hello' 中的 hello ,是hello.js 中定义的 hello 函数
使用function hello() {} 的方式定义一个函数的时候,它就是全局可用的
如果选择了把它 export 给requirejs,那代码依赖于 hello 模块的时候,就可以拿到这个hello 函数的引用了
exports 可以把某个非requirejs方式的代码中的某一个全局变量暴露出去,当作该模块以引用


暴露多个变量:init

如果我要同时暴露多个全局变量呢?比如, hello.js 的定义其实是这样的:

    function hello() {
      alert("hello, world~");
    }
    function hello2() {
      alert("hello, world, again~");
    }

它定义了两个函数,而我两个都想要。这时就不能再用exports了,必须换成 init 函数:

    requirejs.config({
      baseUrl: '/public/js',
      paths: {
        hello: 'hello'
      },
      shim: {
        hello: {
          init: function() {
            return {
              hello: hello,
              hello2: hello2
            }
          }
        }
      }
    });
     
    requirejs(['hello'], function(hello) {
      hello.hello1();
      hello.hello2();
    });

当 exports 与 init 同时存在的时候, exports 将被忽略。