CommonJS、AMD、CMD

CommonJS

CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
规定:
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序。

Node内部提供一个Module构建函数,所有的模块都是Module的实例。

 function Module(id, parent) {
  this.id = id;
  this.exports = {};
  this.parent = parent;
  // ...

module.id 模块的识别符,通常是带有绝对路径的模块文件名。 module.filename 模块的文件名,带有绝对路径。
module.loaded 返回一个布尔值,表示模块是否已经完成加载。 module.parent 返回一个对象,表示调用该模块的模块。
module.children 返回一个数组,表示该模块要用到的其他模块。 module.exports 表示模块对外输出的值。

我们首先要知道,CommonJS是一个规范,是为js模块化制定的,在之前js只能在浏览器端使用的时候,模块化的概念其实用处不是很大,因为浏览器能处理的逻辑毕竟是有限的,现在,再CommonJS的指导下,Node诞生了,将js的环境由浏览器进一步扩展向了服务器端,这时候,模块化就很重要了。我们看到过在Python、Ruby这些脚本语言中,都有模块化概念,可以直接import,包括在java中的包,都是在做这一件事情。现在,Node中使用模块化也可以实现更多复杂逻辑。
其次,模块化的优势在于可以不用担心命名冲突,模块与模块之间是没有直接关系的,内部的变量作用域仅限于模块内,exports的变量除外,这样就能更有效的组织代码,也能更好的团队协作。
最后,开发者不必手动解析模块或者库的依赖项,在之前写代码的时候,我们要想使用一个依赖于Jq的插件,那么我们首先要做的就是先再页面中通过script标签把jq引入,然后再引入插件代码,继而才能使用,现在有了模块化概念,我们可以直接在使用的插件代码中引入jq模块而不必手动去每次引入。
现在我们搞懂了Module的优势,然而,因为CommonJS是为服务器端制定的规范,所以难免有些考虑不周。不周的地方就是同步加载的问题。我们知道,服务器上的文件,基本都存在本地硬盘或者缓存中,当我们导入一个模块的时候,能够很快的从硬盘或者缓存中读取,这样一来,即使是同步加载,速度也可以得到保障。
在客户端情况就不同了。

    var math = require('math');
  math.add(2, 3);

看上面的代码,你一定发现了,math模块要想能够使用,必须在require加载完毕之后才能够使用,否则将抛出undefined错误,那么问题来了,客户端要想加载math模块,就需要向服务器发送请求,其加载的速度与网络环境息息相关,这就意味着,如果加载速度慢,js脚本会停留在这里等待直至加载完成,同步加载的问题已经很明显了。

AMD

这么强大的功能,前端竟然不能使用,这怎么行!
AMD应运而生,是“Asynchronous Module Definition”的缩写,意思就是”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。好吧,看到回调,想必你已经明白了一切!

AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:

require([module], callback);

第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

require(['math'], function (math) {
    math.add(2, 3);
  });

math.add()与math模块加载不是同步的,浏览器不会发生假死。AMD规范的主要实践就是现在前端流行的js库:require.js。

RequireJS就是实现了AMD规范的呢。

一、为什么要用require.js?

最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。

    <script src="1.js"></script>
  <script src="2.js"></script>
  <script src="3.js"></script>
  <script src="4.js"></script>
  <script src="5.js"></script>
  <script src="6.js"></script>

这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

require.js的诞生,就是为了解决这两个问题:

(1)实现js文件的异步加载,避免网页失去响应;   
(2)管理模块之间的依赖性,便于代码的编写和维护。

使用方法,去官网下载require.js,放在js子目录下。加载到页面中即可。

 <script src="js/require.js" defer async="true" ></script>

async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。
加载require.js以后,下一步就要加载我们自己的代码了。假定我们自己的代码文件是main.js,也放在js目录下面。那么,只需要写成下面这样就行了:

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

data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。
具体mainjs的写法不再多说,可以去requirejs官网

CMD

CMD 即Common Module Definition通用模块定义,CMD规范是国内发展出来的,就像AMD有个requireJS,CMD有个浏览器的实现SeaJS,SeaJS要解决的问题和requireJS一样,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同

AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
CMD推崇就近依赖,只有在用到某个模块的时候再去require
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同

如下模块通过SeaJS/RequireJS来加载, 执行结果会是怎样?

define(function(require, exports, module) {
    console.log('require module: main');

    var mod1 = require('./mod1');
    mod1.hello();
    var mod2 = require('./mod2');
    mod2.hello();

    return {
        hello: function() {
            console.log('hello main');
        }
    };
});

先试试SeaJS的执行结果

require module: main
require module: mod1
hello mod1
require module: mod2
hello mod2
hello main

再来是RequireJS的执行结果

require module: mod1
require module: mod2
require module: main
hello mod1
hello mod2
hello main

可以看到,SeaJS更符合我们的逻辑。这里要注意是执行而不是加载,二者都是异步加载的,但是SeaJS只有在使用的时候才解析,而RequireJS加载完成后便立刻解析了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值