You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// filename: foo.js
define(['jquery'], function ($) {
// methods
function myFunc(){};
// exposed public methods
return myFunc;
});
AMD讲究的是前置执行。稍微复杂的例子如下, foo 模块有多个依赖及方法暴漏:
// filename: foo.js
define(['jquery', 'underscore'], function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
});
// filename: foo.js
// dependencies
var $ = require('jquery');
// methods
function myFunc(){};
// exposed public method (single)
module.exports = myFunc;
稍微复杂一点的示例如下,拥有多个依赖以及抛出多个接口:
// filename: foo.js
var $ = require('jquery');
var _ = require('underscore');
// methods
function a(){}; // private because it's omitted from module.exports (see below)
function b(){}; // public because it's defined in module.exports
function c(){}; // public because it's defined in module.exports
// exposed public methods
module.exports = {
b: b,
c: c
};
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'), require('underscore'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery, root._);
}
}(this, function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
}));
// CommonJS模块
let { stat, exists, readFile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
JS中几种模块化规范(AMD、CMD、CommonJS、UMD、ES6的Module语法)的介绍
Javascript的组件生态在最近几年的发展很给力,我们的可选性更加广泛了。这本是一件好事,但是当多个第三方Javascript在一起混合使用的时候,我们可能会遇到一个很尴尬的问题,那就是不是所有的组件都能在一起很愉快的玩耍的。
为了解决这个问题,两种竞争关系的前端模块规范(AMD和CommonJS)问世了。他们规定开发者们采用一种约定好的模式来写代码,以避免污染整个生态系统。
AMD
AMD规范,全称”Asynchronous Module Definition”,称为 异步模块加载规范 。一般应用在浏览器端。流行的浏览器端异步加载库 RequireJS ( 中文网站 )实现的就是AMD规范。
下面是使用AMD规范定义一个名为 foo 模块的方式,此模块依赖jquery:
AMD讲究的是前置执行。稍微复杂的例子如下, foo 模块有多个依赖及方法暴漏:
define 是AMD规范用来声明模块的接口,示例中的第一个参数是一个数组,表示当前模块的依赖。第二个参数是一个回调函数,表示此模块的执行体。只有当依赖数组中的所有依赖模块都是可用的时,AMD模块加载器(比如RequireJS)才会去执行回调函数并返回此模块的暴露接口。
注意,回调函数中参数的顺序与依赖数组中的依赖顺序一致。(即: jquery -> $ , underscore -> _ )
当然,在这里我可以将回调函数的参数名称改成任何我们想用的可用变量名,这并不会对模块的声明造成任何影响。
除此之外,你不能在模块声明的外部使用 $ 或者 _ ,因为他们只在模块的回调函数体中才有定义。
关于AMD规定声明模块的更多内容,请参考 这里 。
CMD
CMD规范,全称”Common Module Definition”,称为 通用模块加载规范 。一般也是用在浏览器端。浏览器端异步加载库 Sea.js 实现的就是CMD规范。
下面是使用AMD规范定义一个名为 foo 模块的方式,此模块依赖jquery:
CMD规范倾向依赖就近,稍微复杂一点例子:
CommonJS
根据CommonJS规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的。
如果你在Node.js平台上写过东西,你应该会比较熟悉CommonJS规范。与前面的AMD及CMD规范不一样的是,CommonJS规范一般应用于服务端(Node.js平台),而且CommonJS加载模块采用的是同步方式(这跟他适用的场景有关系)。同时,得力于 Browserify 这样的第三方工具,我们可以在浏览器端使用采用CommonJS规范的js文件。
下面是使用CommonJS规范声明一个名为 foo 模块的方式,同时依赖 jquery 模块:
稍微复杂一点的示例如下,拥有多个依赖以及抛出多个接口:
UMD
因为AMD,CommonJS规范是两种不一致的规范,虽然他们应用的场景也不太一致,但是人们仍然是期望有一种统一的规范来支持这两种规范。于是,UMD(Universal Module Definition,称之为 通用模块规范 )规范诞生了。
客观来说,这个UMD规范看起来的确没有AMD和CommonJS规范简约。但是它支持AMD和CommonJS规范,同时还支持古老的全局模块模式。
我们来看个示例:
个人觉得UMD规范更像一个语法糖。应用UMD规范的js文件其实就是一个立即执行函数。函数有两个参数,第一个参数是当前运行时环境,第二个参数是模块的定义体。在执行UMD规范时,会优先判断是当前环境是否支持AMD环境,然后再检验是否支持CommonJS环境,否则认为当前环境为浏览器环境( window )。当然具体的判断顺序其实是可以调换的。
下面是一个更加复杂的示例:
Tips: 如果你写了一个小工具库,你想让它及支持AMD规范,又想让他支持CommonJS规范,那么采用UMD规范对你的代码进行包装吧,就像 这样 。
ES6的Module语法
历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量地静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。
由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。
除了静态加载带来的各种好处,ES6 模块还有以下好处。
参考文档
https://www.davidbcalhoun.com/2014/what-is-amd-commonjs-and-umd/
https://www.tuicool.com/articles/nueqi27
http://es6.ruanyifeng.com/#docs/module
The text was updated successfully, but these errors were encountered: