全面理解JS模块的标准(AMD、CMD、COMMON.JS 、 UMD、ESM)
在前端的世界演变里,有着几种JS的模块规范,从出现的顺序来说就是:
①amd
②cmd
③common.js
④ umd
⑤ esm
现在Vue框架里面都是遵守esm规范,不得不说esm是目前最好最流行的一种js规范了
一、amd - 浏览器中的js模块化解决方案
AMD 全称是 Async Module Definition -中文: 异步模块化定义
require.js是AMD模块规范的一个具体实现
1、通过define方法定义模块
base.js
define(function (){
var control = {};
return control;
});
control.js
define(['jquery', 'jqmd5', 'cookie', 'base'], function (){
var control = {};
/**
* 登录状态检测
*/
control.cookie = function (){
setTimeout(WK.LC.syncLoginState, 100);
};
/**
* 模块调用及配置
*/
control.template = function (){
if($('.naver').length > 0) base.naver();
if(CATEGORY == 'login')
{
if(MODEL == 'index'){
// 登录页
require(['login'], function (Login){
Login.form();
});
};
if(MODEL == 'register' || MODEL == 'check'){
// 注册页
require(['register'], function (Register){
Register.form(MODEL);
});
};
};
if(CATEGORY == 'goods')
{
// 详情页
if(MODEL == 'index'){
require(['detail'], function (Detail){
// Detail.form();
});
};
};
};
return control;
});
2、通过require方法加载模块(异步加载)
注意:参数里面有define声明的模块
require(['control'], function (Control){
Control.cookie();
Control.template();
});
二、cmd - 类似amd的用于浏览器中的js模块规范
CMD 全称是 Common Module Definition -中文: 通用模块化定义
sea.js是CMD模块规范的一个具体实现 Sea.js 文档 Seajs github
在定义模块方面, CMD和AMD一样通过define函数来定义模块; 两者的主要区别在于对依赖的加载上, CMD中不需要在define的参数中直接声明需要用到的模块
1、通过define方法定义模块
calculator.js
define('calculator', function(require, exports) {
// 通过require方法加载其他模块
var math = require('math');
exports.add = function(left, right) { return math.add(left, right) };
exports.subtract = function(left, right) { return math.subtract(left, right) };
})
可以看到calculator模块所的依赖的math模块没有在define函数的参数中进行声明, 而是通过require(‘math’)来引入的
2、使用calculator模块
seajs.use(['calculator'], function(calculator) {
console.log('1 + 1 = ' + calculator.add(1, 1));
console.log('2 - 2 = ' + calculator.subtract(2, 1));
})
三、common.js -Node中使用的模块规范
通过exports和module.exports来暴露模块中的内容。
通过require来加载模块。
demo
1、通过exports 暴露模块接口
study.js
var hello = function () {
console.log('hello studygd.com.');
}
exports.hello = hello;
main.js
const studygd = require('./study');
studygd.hello();
2、通过module.exports 暴露模块接口
定义math模块
math.js
module.exports = {
add: function(left, right) {
return left + right;
},
subtract: function(left, right) {
return left - right;
}
}
使用刚才定义的math模块, 并再定义一个calculator模块
calculator.js
const math = require('./math.js');
module.exports = {
add: math.add
}
四、umd - 一种同时兼容了amd cmd common.js的规范
amd cmd 通常只能在浏览器中使用, commonjs只能在服务端(Node)**环境下使用, 这样子搞会导致我们基于其中某一种模块规范写的js模块无法在服务端和浏览器端进行复用.
umd解决了这个问题, 它兼容并包, 使得使用此规范写的 js模块既可以在浏览器环境下使用, 也可以在Node(服务端)环境中用
(function (root, factory) {
if (typeof exports === 'object' && typeof module === 'object')
// commonjs
module.exports = factory()
else if (typeof define === 'function' && define.amd)
// amd、cmd
define([], factory)
else if (typeof exports === 'object')
// commonjs
exports['math'] = factory()
else
// 全局对象, 浏览器中是 window
root['math'] = factory()
})(this, function() {
return { add: function(left, right) { return left + right; } }
})
*其实只要你看过jq源码,你就会觉得 上面的这段代码很熟悉,是的,jq源码里面就是采用了umd规范去做兼容,所以jq可以说是umd规范的一种代表
附:jq源码大体框架
( function( global, factory ) {
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments where a proper `window`
// is present, execute the factory and get jQuery.
// For environments that do not have a `window` with a `document`
// (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info.
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
// Pass this if window is not defined yet
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
//这里编写jquery主体代码...
// AMD
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function() {
return jQuery;
} );
}
var
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$;
jQuery.noConflict = function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
};
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
} );
五、esm - ES6模块规范
使用import导入模块,通过export导出模块
math.js
export { add: (left, right) => left + right; }
在calculator.js导入
import { add } from './math.js';
console.log('1 + 1 = ' + add(1, 1));
总结
amd, cmd曾经是个很流行的js模块化产物,记得自己出来工作不久,去面试的时候,也被问过这个两个有什么不一样(上面已经答案),到底使用哪个好等问题,那时自己也是模糊不清,具体回答的不是很清楚,也许是当时那个公司在使用这些规范才考察我,不过现在amd、cmd感觉基本上是没有公司项目在使用了, 现在常用的模块规范一般就是es6模块和commonjs(只用于node)了, node中也已经提供了实验性的es模块支持.
随着微软放弃IE ,现代浏览器对es的import和export的支持也已经很不错了(除了IE其他主流浏览器都支持了)