JS 模块化 AMD CMD UMD Cjs ES Module 你真的分清楚了吗

971 阅读3分钟
javascript 模块化的发展历程

JS 模块化开发有较长的历史,早起流行的命名空间 的开发思想,到后来有了一些模块化的开发规范,最先是CommonJS(主要使用在NodeJS 不是和浏览器),后来AMD、CMD、UMD、ESM等规范相继诞生。因为JS并未提供一种原生的,语言级别的模块化开发模式,而是将模块化交给开发者实现,所以这些规范的诞生,让JS模块化开发变得规范。

CommonJS
  • 一个文件一个模块;
  • 使用 exports.xx = ... 或者 module.exports ={} 暴露模块;
  • 使用 require() 方法引入一个模块;
  • require()是同步执行的;

示例

// a.js
let fn=function(msg){
  console.log(msg);
}
exports.outMsg=fn;
//b.js
const a=require('./a.js');
a.outMsg('hello Commonjs'); // hello Commonjs

CommonJS 在NodeJS 环境用,不适用于浏览器;
一个文件一个模块;如果有多个导出,则取最后一个导出;

AMD

全称 Asynchronous module definition(异步模块定义)

  • 使用 define(...) 定义一个模块;
  • 使用require(...) 加载一个模块;
  • 依赖前置,提前执行; RequireJS 是AMD 的一种实现 示例
// 模块的定义
/**
*@param id 模块名称,如果为空,模块的名字默认为模块加载器请求的制定脚本名
*@param dependencies 模块依赖
*@param factory 工厂函数,模块初始化执行函数或对象
*/
define(id,dependencies,factory)
// 模块的使用,使用 require 加载模块
require([module],callback);

CMD

全称 Common Module Definition(通用模块定义)

  • 一个文件为一个模块
  • 使用 define(...) 定义一个模块 (和AMD相似)
  • 使用require(...) 加载一个模块(和AMD 相似) SeaJS 是CMD 的一种实现 示例:
// CMD 
define(function(require,exports,module){
  var a=require('./a');
  a.sayHello();
  var b=require('./b');// 依赖就近书写;
  b.sayHello();
  //...
});

CMD和AMD 的最显著的区别 AMD 是提前执行,CMD 是延迟执行,依赖就近;

AMD: 执行过程中会将所有的依赖模块前置执行,也就是自己的代码逻辑开始前全部执行;

CMD :如果require 但整个逻辑未使用这个依赖 或者为执行到逻辑使用它的地方前,不会执行。

UMD

全称 Universal Module Definition(万能模块定义),从名字就可以看出 UMD 做的是大一统的工作。Webpack 打包代码就有 UMD 这个选项。

这个万能模块,可以在服务端使用,也可以在浏览器端使用;

它主要做了三件事:

  • 判断是否支持AMD
  • 判断是否支持CommonJS
  • 如果都不支持,使用全局变量

主要代码如下:

(function (root, factory) {
    // 对应上述的三个步骤
    if (typeof define === 'function' && define.amd) {
        // 1.判断是否支持 AMD
        // 如果 define 这个方法是被定义 并且 define 这个方法是 AMD 的规范,那就把 factory 这个模块实体用 define 方法以 AMD 的规范 定义
        define([], factory); // [] 是依赖,factory 是模块实体
    } else if (typeof exports === 'object') {
        // 2. 判断是否支持 CommonJS
        // 如果 exports 是等于一个对象,则表明是在 Node 环境中运行,则支持 CommonJS,那就用 module.exports 暴露整个模块实体
        module.exports = factory();
    } else {
        // 3. 如果都不支持,使用全局变量
        // Browser globals (root 即是 window)
        root.returnExports = factory();
  }
}(this, function () {
    // Module Defination
    var sum = function(x, y){
        return x + y;
    }
    var sub = function(x, y){
        return x - y;
    }
    var math = {
        findSum: function(a, b){
            return sum(a,b);
        },
        findSub: function(a, b){
            return sub(a, b);
        }
    }
    return math;
}));

ES Module (ES6 模块)

全称 ECMAScript Module

  • 使用 import 导入模块;
  • 使用 export 导出模块;

示例:

// 导出模块
export var a='123'; //导出变量
export function fn(){}; // 导出函数
export default {name:'p',age:18} // 导出对象; export 不能直接导出对象必须加上default;
export class Myclass{} // 导出类;

ES6 模块和Common js 模块的差异:

​ 1、CommonJS 输出的是一个值的拷贝;ES6 模块输出的是值的引用;

​ 2、CommonJS 模块是运行时加载;ES6模块是编译时输出接口;

由于ES6 模块是编译时输出接口,所以可以做到 tree shaking;