前端发展到今天,已经有不少模块化的方案,比如 AMD 、 CMD 、 UMD 、 CommonJS 等,当然了,还有 es6 带来的模块系统,这些模块化规范的核心价值都是让 JavaScript 的模块化开发变得简单和自然,今天就来看看这些规范都是啥。
在模块化这东西没出来之前,前端脚本引用大概是这样的:
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>
...
...
...
模块把接口暴露到全局对象下(比如 window ),各个模块可以通过全局对象访问各个依赖的接口,但是也存在一些问题:
script 标签就会特别多,并且难以维护。比如: <script src="module1.js"></script>
<script src="module2.js"></script>
<script src="module3.js"></script>
<script src="module4.js"></script>
<script src="module5.js"></script>
<script src="module6.js"></script>
<script src="module7.js"></script>
<script src="module8.js"></script>
<script src="module9.js"></script>
<script src="module10.js"></script>
<script src="module11.js"></script>
<script src="module12.js"></script>
<script src="module13.js"></script>
<script src="module14.js"></script>
<script src="module15.js"></script>
...
为了解决这些问题,各种模块化的方案都出来了
这种方式通过一个叫做 require 的方法,同步加载依赖,然后返导出 API 供其它模块使用,一个模块可以通过 exports 或者 module.exports 导出 API 。 CommonJS 规范中,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,在一个文件中定义的变量,都是私有的,对其他文件是不可见的。
require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;
服务端 Node.js 就是用的这种方式。
npm 上基本上都是这种风格的 module 像 Node.js 主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以 CommonJS 规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD 、 CMD 的解决方案。
AMD === Asynchronous Module Definition
介于上面的说到的问题,于是浏览器端的异步版本的 require 应运而生
require(["module", "../file"], function(module, file) { /* ... */ });
define("mymodule", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
AMD 规范中, define 函数有一个公有属性 define.amd 。
AMD 被使用的最广泛的实现方案无疑就是 require.js 了
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出
CMD 规范中定义了 define 函数有一个公有属性 define.cmd 。
CMD 模块中有两种方式提供对外的接口,一种是 exports.MyModule = ... ,一种是使用 return 进行返回。
CMD 和 AMD 的区别有以下几点:
对于依赖的模块 AMD 是提前执行, CMD 是延迟执行。( require2.0 貌似有变化)
2. CMD 推崇依赖就近, AMD 推崇依赖前置。
//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();
}
});
虽然 AMD 也支持 CMD 写法,但依赖前置是官方文档的默认模块定义写法。
推荐一篇文章: SeaJS与RequireJS最大的区别
UMD 是 AMD 和 CommonJS 两者的结合, AMD 浏览器第一的原则发展,异步加载模块。 CommonJS 模块以服务器第一原则发展,同步加载模块,它的模块无需包装。
但是我如果想同时支持两种风格呢?于是通用模块规范( UMD )诞生了。这个模式中加入了当前存在哪种规范的判断,所以能够“通用”,它兼容了 AMD 和 CommonJS ,同时还支持老式的“全局”变量规范:
(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD
define(["jquery"], factory);
} else if (typeof exports === "object") {
// Node, CommonJS之类的
module.exports = factory(require("jquery"));
} else {
// 浏览器全局变量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
// 方法
function test(){};
// 暴露公共方法
return test;
}));
ES6 为 JavaScript 添加了一些语言结构,形成另一个模块系统。
import "jquery";
export function doStuff() {}
module "localModule" {}
Babel ) 本文主要是介绍了一下 AMD 、 CMD 等规范,较为笼统,下面的扩展阅读可以更好的帮助你理解模块化以及各个规范。
模块系统
前端模块化开发的价值
前端模块化开发那点历史
CMD模块定义规范
SeaJS API快速参考
从CommonJS到Sea.js
RequireJS和AMD规范
CommonJS规范
Javascript模块化编程
Javascript模块化编程
知乎AMD和CMD的区别有哪些?
JavaScript模块化开发 - CommonJS规范
JavaScript模块化开发 - AMD规范