JS中的模块规范(CommonJS,AMD,CMD),若是你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过。若是仅仅是一些基础的活,咱们熟悉它的使用就好,可是咱们不能对一块只是朦朦胧胧,今天我就来整理一下,这些究竟是什么?它们在咱们前端的领域到底起了怎样的做用?前端
CommonJS就是为JS的表现来制定规范,NodeJS是这种规范的实现,webpack 也是以CommonJS的形式来书写。由于js没有模块的功能因此CommonJS应运而生,它但愿js能够在任何地方运行,不仅是浏览器中。它的终极目标是提供一个相似Python,Ruby和Java标准库。node
若是你在Node.js平台上写过东西,你应该会比较熟悉CommonJS规范。与前面的AMD及CMD规范不同的是,CommonJS规范通常应用于服务端(Node.js平台),并且CommonJS加载模块采用的是同步方式(这跟他适用的场景有关系)。同时,得力于 Browserify 这样的第三方工具,咱们能够在浏览器端使用采用CommonJS规范的js文件。webpack
CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}
require()用来引入外部模块;
exports对象用于导出当前模块的方法或变量,惟一的导出口;
module对象就表明模块自己。
复制代码
虽然说Node遵循CommonJS的规范,可是相比也是作了一些取舍,填了一些新东西的。git
不过,说了CommonJS也说了Node,那么我以为也得先了解下NPM了。NPM做为Node的包管理器,不是为了帮助Node解决依赖包的安装问题嘛,那它确定也要遵循CommonJS规范啦,它遵循包规范(仍是理论)的。github
CommonJS主要是为了JS在后端制定的,那它为何不适合前端呢?
web
那咱们就来看看它的原理:
后端
浏览器不兼容CommonJS的根本缘由,在于缺乏四个Node.js环境的变量。
module
exports
require
global
复制代码
只要可以提供这四个变量,浏览器就能加载 CommonJS 模块。数组
知道了原理,就能够作出工具,Browserify是目前最经常使用的 CommonJS 格式转换的工具。(有兴趣的同窗能够去深刻了解)浏览器
基于commonJS规范的nodeJS出来之后,服务端的模块概念已经造成,很天然地,你们就想要客户端模块。并且最好二者可以兼容,一个模块不用修改,在服务器和浏览器均可以运行。可是,因为一个重大的局限,使得CommonJS规范不适用于浏览器环境。看以下代码,若是在浏览器中运行,会有一个很大的问题,你能看出来吗?bash
var math = require('math');
math.add(2, 3);
复制代码
第二行math.add(2, 3),在第一行require('math')以后运行,所以必须等math.js加载完成。也就是说,若是加载时间很长,整个应用就会停在那里等。您会注意到 require是同步的。这对服务器端不是一个问题,由于全部的模块都存放在本地硬盘,能够同步加载完成,等待时间就是硬盘的读取时间。可是,对于浏览器,这倒是一个大问题,由于模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。
所以,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。
CommonJS是主要为了JS在后端的表现制定的,他是不适合前端的,AMD(异步模块定义)出现了,它就主要为前端JS的表现制定规范
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比较适合浏览器环境。目前,主要有两个Javascript库实现了AMD规范:require.js和curl.js。
require.js的诞生,就是为了解决这两个问题:
(1)实现js文件的异步加载,避免网页失去响应;
(2)管理模块之间的依赖性,便于代码的编写和维护。
固然我以为每一个库的诞生都有它自身的价值。
AMD就只有一个接口:define(id?,dependencies?,factory);
它要在声明模块的时候制定全部的依赖(dep),而且还要当作形参传到factory中,像这样:
define(['dep1','dep2'],function(dep1,dep2){...});
复制代码
要是没什么依赖,就定义简单的模块,下面这样就能够啦:
define(function(){
var exports = {};
exports.method = function(){...};
return exports;
});
复制代码
咦,这里有define,把东西包装起来啦,那Node实现中怎么没看到有define关键字呢,它也要把东西包装起来呀,其实吧,只是Node隐式包装了而已.....
这有AMD的WIKI中文版,讲了不少蛮详细的东西,用到的时候能够查看:AMD WIKI 中文版
CMD规范,全称”Common Module Definition”,称为 通用模块加载规范 。通常也是用在浏览器端。浏览器端异步加载库 Sea.js 实现的就是CMD规范。
AMD/CMD区别,虽然都是并行加载js文件,但仍是有所区别,AMD是预加载,在并行加载js文件同时,还会解析执行该模块(由于还须要执行,因此在加载某个模块前,这个模块的依赖模块须要先加载完成);而CMD是懒加载,虽然会一开始就并行加载js文件,可是不会执行,而是在须要的时候才执行。
在 CMD 规范中,一个模块就是一个文件
define(id?, deps?, factory)
复制代码
id:模块标识
deps:模块依赖
factory:能够是一个函数,也能够是一个对象或字符串。
复制代码
具体更详细的规范,能够参考CMD规范
有什么遗漏的,欢迎补充~