关于COMMONJS | AMD | CMD | UMD 模块化理解
前言
历史上,js没有模块化的概念,不能把一个大工程分解成很多小模块。这对于多人开发大型,复杂的项目形成了巨大的障碍,明显降低了开发效率,java,Python有import,甚至连css都有@import,但是令人费解的是js居然没有这方面的支持。es6出现之后才解决了这个问题,在这之前,各大社区也都出现了很多解决方法,比较出色的被大家广为流传的就有AMD, CMD, CommonJs, UMD,今天就来分析这几个模块化的解决方案。
模块加载
上面提到的几种模块化的方案的模块加载有何异同呢?
先来说下es6模块,es6模块的设计思想是尽量静态化,使得编译时就能确定依赖关系,被称为编译时加载。其余的都只能在运行时确定依赖关系,这种被称为运行时加载。下面来看下例子就明白了,比如下面这段代码:
let {a,b,c} = require("util"); //会加载util里的所有方法,使用时只用到3个方法
import {a,b,c} from 'util'; //从util中加载3个方法,其余不加载
模块化方案
1、COMMONJS
commonjs是服务端模块化采用的规范,nodejs就采用了这个规范。
根据commonjs的规范,一个单独的文件就是一个模块,加载模块使用require方法,该方法读取文件并执行,返回export对象。
// 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;
// 读取
var test = require('./foobar').foobar;
test.bar();
CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD CMD 解决方案。
2、AMD
// API
define(id?, dependencies?, factory);
id 为字符串类型,表示了模块标识,为可选参数。若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。如果存在,那么模块标识必须为顶层的或者一个绝对的标识。
dependencies ,是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。
factory,是一个需要进行实例化的函数或者一个对象。
// example
define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
export.verb = function(){
return beta.verb();
// or:
return require("beta").verb();
}
});
提到 AMD 就不得不提 requirejs;
RequireJS 是一个前端的模块化管理的工具库,遵循AMD规范,AMD的基本思想就是先加载需要的模块,然后返回一个新的函数,所有的操作都在这个函数内部操作,之前加载的模块在这个函数里是可以调用的。
3、CMD
CMD是seajs在推广的过程中对模块的定义的规范化产出
和AMD提前执行不同的是,CMD是延迟执行,不过requirejs从2.0开始也开始支持延迟执行了,这取决于写法。
AMD推荐的是依赖前置,CMD推荐的是依赖就近。
// 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();
}
});
4、UMD
UMD是AMD和commonjs的结合
AMD适用浏览器,commonjs适用服务端,如果结合了两者就达到了跨平台的解决方案。
UMD先判断是否支持AMD(define是否存在),存在用AMD模块的方式加载模块,再判断是否支持nodejs的模块(exports是否存在),存在用nodejs模块的方式,否则挂在window上,当全局变量使用。
(function(window, factory) {
//amd
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') { //umd
module.exports = factory();
} else {
window.jeDate = factory();
}
})(this, function() {
...module..code...
})
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。