前言
我们在开发及接触新技术的时候,总会接触到该技术以何种规范编写,为了更加系统了解这些规范,本文总结了AMD,CMD,CommonJs,UMD,ESM几种规范,供大家学习了解。
AMD
AMD(Asynchronous Module Definition),异步模块定义
AMD
是RequireJS
在推⼴过程中对模块定义的规范化
产出,它是⼀个概念,RequireJS是对这个概念的实现,就好⽐JavaScript语⾔是对ECMAScript规范的实现。AMD是⼀个组织,RequireJS是在这个组织下⾃定义的⼀套脚本语⾔
RequireJS:是⼀个AMD框架,可以异步加载JS⽂件,按照模块加载⽅法,通过define()函数定义。第⼀个参数是⼀个数组,⾥⾯定义⼀些需要依赖的包,第⼆个参数是⼀个回调函数,通过变量来引⽤模块⾥⾯的⽅法,最后通过return来输出()。
是⼀个依赖前置
、异步定义
的AMD框架(在参数⾥⾯引⼊js⽂件),在定义的同时如果需要⽤到别的模块,在最前⾯定义好即在参数数组⾥⾯进⾏引⼊,在回调⾥⾯加载
- 特点:
1、异步加载
2、管理模块之间的依赖性,便于代码的编写和维护。
- 环境:浏览器环境
- 应⽤:requireJS是参照AMD规范实现的
- 语法:
1、导出:define(function (){return '值');
2、导⼊:require(['模块名称'], function ('模块变量引⽤'){// 代码});
// a.js
define(function (){
return {
a:'hello world'
}
});
// b.js
require(['./a.js'], function (moduleA){
console.log(moduleA.a); // 打印出:hello world
});
AMD 定义了一套 JavaScript 模块依赖异步加载标准,来解决同步加载的问题。模块通过 define 函数定义在闭包中,格式如下:
define(id?: String, dependencies?: String[], factory: Function|Object);
- id 是模块的名字,它是可选的参数。
- dependencies 指定了所要依赖的模块列表,它是一个数组,也是可选的参数,每个依赖的模块的输出将作为参数一次传入 factory 中。如果没有指定 dependencies,那么它的默认值是 ["require", "exports", "module"]。
define(function(require, exports, module) {})
- factory 是最后一个参数,它包裹了模块的具体实现,它是一个函数或者对象。如果是函数,那么它的返回值就是模块的输出接口或值。
一些栗子:
- 定义一个名为 myModule 的模块,它依赖 jQuery 模块:
define('myModule', ['jquery'], function($) {
// $ 是 jquery 模块的输出
$('body').text('hello world');
});
// 使用
require(['myModule'], function(myModule) {});
注意:在 webpack 中,模块名只有局部作用域,在 Require.js 中模块名是全局作用域,可以在全局引用。
定义一个没有 id 值的匿名模块,通常作为应用的启动函数:
define(['jquery'], function($) {
$('body').text('hello world');
});
依赖多个模块的定义:
define(['jquery', './math.js'], function($, math) {
// $ 和 math 一次传入 factory
$('body').text('hello world');
});
模块输出:
define(['jquery'], function($) {
var HelloWorldize = function(selector){
$(selector).text('hello world');
};
// HelloWorldize 是该模块输出的对外接口
return HelloWorldize;
});
在模块定义内部引用依赖:
define(function(require) {
var $ = require('jquery');
$('body').text('hello world');
});
- 参考文章:
CMD
CMD(Common Module Definition) 通用模块定义
SeaJS
在推⼴过程中对模块定义的规范化产出,是⼀个同步模块定义,是SeaJS的⼀个标准,SeaJS是CMD概念的⼀个实现,SeaJS是淘宝团队提供的⼀个模块开发的js框架.
通过define()定义,没有依赖前置
,通过require加载模块,CMD是依赖就近
,在什么地⽅使⽤到模块就在什么地⽅require该模块,即⽤即返,这是⼀个同步
的概念
- 特点
1、CMD是在AMD基础上改进的⼀种规范,和AMD不同在于对依赖模块的执⾏时机处理不同,CMD是就近依赖,⽽AMD是前置依赖。
- 环境:浏览器环境
- 应⽤:seajs是参照UMD规范实现的,requireJS的最新的⼏个版本也是部分参照了UMD规范的实现
语法:
1、导⼊:define(function(require, exports, module) {});
2、导出:define(function (){return '值');
// a.js
define(function (require, exports, module){
exports.a = 'hello world';
});
// b.js
define(function (require, exports, module){
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出:hello world
});
- 参考文章
CommonJS
在前端浏览器⾥⾯并不⽀持module.exports,CommonJS 是以在浏览器环境之外构建 JavaScript 生态系统为目标而产生的项目,比如在服务器和桌面环境中。
Nodejs端是使⽤CommonJS规范的,前端浏览器⼀般使⽤AMD、CMD、ES6等定义模块化开发的。输出⽅式有2种:默认输出module export 和带有名字的输出exports.area
CommonJS 规范是为了解决 JavaScript 的作用域问题而定义的模块形式,可以使每个模块它自身的命名空间中执行。该规范的主要内容是,模块必须通过 module.exports 导出对外的变量或接口,通过 require() 来导入其他模块的输出到当前模块作用域中。
- 特点:
1、模块可以多次加载,但是
只会在第⼀次加载时运⾏⼀次
,然后运⾏结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运⾏,必须清除缓存。
2、模块加载会阻塞接下来代码的执⾏,需要等到模块加载完成才能继续执⾏——同步加载
。
- 环境:服务器环境
- 应⽤:nodejs的模块规范是参照commonJS实现的。
- 语法:
1、导出:module.exports和exports
2、导⼊:require('路径')
- 注意:module.exports和exports的的区别是
exports只是对module.exports的⼀个引⽤
,相当于Node为每个模块提供⼀个exports变量,指向module.exports。这等同在每个模块头部,有⼀⾏var exports = module.exports;
这样的命令。
// a.js
// 相当于这⾥还有⼀⾏:var exports = module.exports;代码
exports.a = 'Hello world'; // 相当于:module.exports.a = 'Hello world';
// b.js
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出hello world
UMD
兼容AMD和commonJS规范的同时,还兼容全局引⽤的⽅式
- 特点:
1、兼容AMD和commonJS规范的同时,还兼容全局引⽤的⽅式
- 环境:浏览器或服务器环境
- 应⽤:⽆
- 语法:
1、⽆导⼊导出规范,只有如下的⼀个常规写法:
(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 myFunc(){};
//暴露公共⽅法
return myFunc;
}));
ESM(ES6Module)
- 特点:
1、
按需加载(编译时加载)
2、import和export命令只能在模块的顶层,不能在代码块之中(如:if语句中),import()
语句可以在代码块中实现异步动态按需动态加载
- 环境:浏览器或服务器环境(以后可能⽀持)
- 应⽤:ES6的最新语法⽀持规范
- 语法:
1、导⼊:import {模块名A,模块名B...} from '模块路径'
2、导出:export和export default
3、import('模块路径').then()⽅法
- 注意:
1、export只⽀持对象形式导出,不⽀持值的导出,export default命令⽤于指定模块的默认输出,只⽀持值导出,但是只能指定⼀个,本质上它就是输出⼀个叫做default的变量或⽅法。
2、import()
语句允许您仅在需要时动态加载模块,而不必预先加载所有模块。import()
作为函数调用,它返回一个promise
,它用一个模块对象来实现,让你可以访问该对象的导出。
/*错误的写法*/
// 写法⼀
export 1;
// 写法⼆
var m = 1;
export m;
// 写法三
if (x === 2) {
import MyModual from './myModual';
}
/*正确的三种写法*/
// 写法⼀
export var m = 1;
// 写法⼆
var m = 1;
export {m};
// 写法三
var n = 1;
export {n as m};
// 写法四
var n = 1;
export default n;
// 写法五
if (true) {
import('./myModule.js')
.then((module) => {
// Do something with the module.
});
}
// 写法六
Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
//···
});