浅析JS模块规范:AMD,CMD,CommonJS

AMD,CMD,CommonJS是目前最常用的三种模块化书写规范

CommonJS

  • CommonJS规范是诞生比较早的。NodeJS就采用了
  • 根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象
  • CommonJS
    加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用

CommonJS。是这样加载模块:

/ foobar.js

//私有变量
var test = 123;

//公有方法
function foobar () {

    this.foo = function () {
        // do someing ...
    }
    this.bar = function () {
        //do someing ...
    }
}

//exports对象上的方法和变量是公有的
var foobar = new foobar();
exports.foobar = foobar;


//require方法默认读取js文件,所以可以省略js后缀
var test = require('./foobar').foobar;

test.bar();

   var clock = require('clock');
   clock.start();

这种写法适合服务端,因为在服务器读取模块都是在本地磁盘,加载速度很快。但是如果在客户端,加载模块的时候有可能出现“假死”状况。比如上面的例子中clock的调用必须等待clock.js请求成功,加载完毕。那么,能不能异步加载模块呢

AMD
  • AMD,即 (Asynchronous Module Definition),这种规范是异步的加载模块,requireJs应用了这一规范。
  • AMD 标准:
    https://github.com/amdjs/amdjs-api/blob/master/AMD.md
  • 先定义所有依赖,然后在加载完成后的回调函数中执行:
  • AMD异步加载模块。它的模块支持对象 函数 构造器 字符串 JSON等各种类型的模块
    适用AMD规范适用define方法定义模块
//通过数组引入依赖 ,回调函数通过形参传入依赖
define(['someModule1', ‘someModule2’], function (someModule1, someModule2) {

    function foo () {
        /// someing
        someModule1.test();
    }

    return {foo: foo}
});

AMD规范允许输出模块兼容CommonJS规范,这时define方法如下:

define(function (require, exports, module) {
    
    var reqModule = require("./someModule");
    requModule.test();
    
    exports.asplode = function () {
        //someing
    }
});

require([module], callback);

用AMD写上一个模块

 require(['clock'],function(clock){
  clock.start();
});

AMD虽然实现了异步加载,但是开始就把所有依赖写出来是不符合书写的逻辑顺序的,能不能像commonJS那样用的时候再require,而且还支持异步加载后再执行呢

CMD

CMD (Common Module Definition), 是seajs推崇的规范,CMD则是依赖就近,用的时候再require。它写起来是这样的
CMD 标准: https://github.com/cmdjs/specification/blob/master/draft/module.md

//AMD
define(['./a','./b'], function (a, b) {

    //依赖一开始就写好
    a.test();
    b.test();
});

//CMD
define(function (requie, exports, module) {
    
    //依赖可以就近书写
    var a = require('./a');
    a.test();
    
    ...
    //软依赖
    if (status) {
    
        var b = requie('./b');
        b.test();
    }
});
define(function(require, exports, module) {
   var clock = require('clock');
   clock.start();
});
3.AMD和CMD最大的区别
  • 是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块
  • -可以很明显的看出RequireJS的做法是并行加载所有依赖的模块, 并完成解析后, 再开始执行其他代码, 因此执行结果只会"停顿"1次, 完成整个过程是会比SeaJS要快
  • 而SeaJS一样是并行加载所有依赖的模块, 但不会立即执行模块, 等到真正需要(require)的时候才开始解析, 这里耗费了时间,
    因为这个特例中的模块巨大, 因此造成"停顿"2次的现象, 这就是我所说的SeaJS中的"懒执行"
  • AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略
  • seajs 与 requirejs 在模块的加载方面是没有差异的,无论是 requirejs 在定义模块时定义的依赖模块,还是 seajs
    在 factory 函数中 require 的依赖模块,在会在加载当前模块时被载入,异步,并且顺序不可控。差异在于 factory
    函数执行的时机
  • AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的
    require,提供 seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹
  • 需要提一下的是二者对待依赖模块的加载是一致的,在factory执行时,依赖模块都已被加载。从代码上来看,AMD中在BEGIN处a、b的factory都是执行过的;而CMD中虽然a、b模块在BEGIN已被加载,但尚未执行,需要调用require执行依赖模块。这就是CMD中着重强调的延迟执行
  • AMD与CMD都借鉴了CommonJs,宏观层面必有一致性,比如整体处理流程:

在这里插入图片描述
模块的加载解析到执行过程一共经历了6个步骤:

1、由入口进入程序

2、进入程序后首先要做的就是建立一个模块仓库(这是防止重复加载模块的关键),JavaScript原生的object对象最为适合,key代表模块Id,value代表各个模块,处理主模块

3、向模块仓库注册一模块,一个模块最少包含四个属性:id(唯一标识符)、deps(依赖项的id数组)、factory(模块自身代码)、status(模块的状态:未加载、已加载未执行、已执行等),放到代码中当然还是object最合适

4、模块即是JavaScript文件,使用无阻塞方式(动态创建script标签)加载模块

scriptElement= document.createElement('script');

scriptElement.src=moduleUrl;

scriptElement.async=true;

scriptElement.onload=function(){.........};

document.head.appendChild(scriptElement);


5、模块加载完毕后,获取依赖项(amd、cmd区别),改变模块status,由statuschange后,检测所有模块的依赖项

由于requirejs与seajs遵循规范不同,requirejs在define函数中可以很容易获得当前模块依赖项。而seajs中不需要依赖声明,所以必须做一些特殊处理才能否获得依赖项。方法将factory作toString处理,然后用正则匹配出其中的依赖项,比如出现require(./a),则检测到需要依赖a模块。

在这里插入图片描述
同时满足非阻塞和顺序执行就需要需要对代码进行一些预处理,这是由于CMD规范和浏览器环境特点所决定的

6、如果模块的依赖项完全加载完毕(amd中需要执行完毕,cmd中只需要文件加载完毕,注意这时候的factory尚未执行,当使用require请求该模块时,factory才会执行,所以在性能上seajs逊于requirejs),执行主模块的factory函数;否则进入步骤3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值