CommonJS

CommonJS同步加载模式,主要应用场景为服务端,通常应用于Node.js的模块化输出与引用。核心的模块化环境变量有moduleexportsrequireglobal

  • require命令用于提供模块引用。
  • module.exports命令用以提供规范模块的输出。(创建后被引用,会创建引用缓存)
// a.js
const text = 'hello world';
const printText = (name) => {
    console.log(text);
    console.log('my name is ', name);
};

module.exports = {
    printText
}
// b.js
const { printText } = require('./a.js');

printText('zerlous');
// hello world
// my name is zerlous
  • CommonJS主要采用同步加载模块,并且应用于服务端,大部分加载的文件资源都在本地服务器,所以执行效率很高。
  • 不适用于客户端(浏览器端)。

AMD

AMD (Asynchronous Module Definition)为异步加载模式,模块的加载不会产生阻塞,所有依赖该模块的的依赖语法都会在一个回调函数callback中,只有等依赖模块加载完成后,才会执行该回调函数。(* 核心RequireJS)。

核心的模块功能命令有definerequirereturndefine.amd

  • define为全局环境变量下的全局函数,用于定义模块。eg:define(id?, dependencies?, factory)
  • require命令用于提供模块引用。
  • return命令用以提供规范模块的输出。
  • define.amd属性是一个对象,此属性的存在来表明函数遵循AMD 规范
// a.js
define(function() {
  const text = 'hello world';
  const printText = (name) => {
    console.log(text);
     console.log('my name is ', name);
  };
  return {
    printText
  };
});
// app.js
define(function () {
  const a_model = require('./a.js');
  a_model.printText('zerlous');
  // hello world
  // my name is zerlous
});
<script src="https://cdn.bootcss.com/require.js/2.3.6/require.min.js"></script>
<script>
    requirejs(['app']);
</script>

CMD

CMD(Common Module Definition)为通用模块定义,同时适用于服务端及客户端。(* 核心Sea.js)。 CMD模块加载模式与AMD类似,但是有差异。

  • AMD依赖前置、提前执行。

    AMD在加载模块完成后,立即会执行该模块,所有模块加载执行完成后,执行callback。主流程一定是在所有依赖加载完成后执行的,但是依赖模块的执行顺序是依赖网络,并非书写顺序

  • CMD依赖就近、延迟执行

    CMD在加载完模块后,并不会立即执行,所有模块加载执行完成后,执行callback。在callback中,遇到require再执行对应的模块,执行顺序与书写顺序保持一致

// a.js
define(function(require, exports, module) {
  const text = 'hello world';
  const printText = (name) => {
    console.log(text);
     console.log('my name is ', name);
  };
  exports.printText = printText;
});
// b.js
define(function(require, exports, module) {
  const printDate = () => {
     console.log('print datastamp is: ', (new Date())*1));
  };
  exports.printDate = printDate;
});
// app.js
define(function (require, exports, module) {
  const a_model = require('./a.js');
  a_model.printText('zerlous');
  // hello world
  // my name is zerlous
  const b_model = require('./b.js');
  b_model.printDate();
  // print datastamp is: 1621111111111
});
<script src="https://cdn.bootcss.com/seajs/3.0.3/sea.js"></script>
<script>
    seajs.use('./app.js')
</script>

UMD

UMD(*Universal Module Definition *)为通用模块定义,主要解决CommonJSAMD两种模式下代码不通用的问题,同时支持老式的全局变量规范。

  • ⭐️⭐️⭐️AMD规范。 define为函数类型,且存在define.amd对象.
  • ⭐️⭐️⭐️CommonJS规范。module为对象类型,且存在module.exports.
// app.js
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global = global || self, global.app = factory()); // 原始代码
}(this, (function () { 'use strict';
    const app = () => {
        return 'hello world';
    };
    return app;
})));
<!-- index.html -->
<script src="app.js"></script>
<script>
  console.log(app());
  // hello world
</script>

ES Modules

ESM(ECMA Script Modules)为javascript官方的标准化模块系统。 同时适用于服务端和客户端。是当前主流使用的多浏览器兼容的模块管理系统。

  • 通过importexport来确定模块的导入导出。且与CommonJS模块可混合使用。
  • ESM输出的是值的引用,输出接口动态绑定。而CommonJS输出的是值的拷贝。
  • ESM编译时执行。 而CommonJS模块总是在运行时加载。

ESM引用输出与CommonJS值拷贝输出的区别?

  • ESM值引用输出, 引用模块内的作用域是维持的。引用方法是会重复执行的。
  • CommonJS值拷贝缓存,重复引用不会重复执行,获取的是模块缓存。
ESM引用输出
// a.js
import { age } from './b.js';

console.log(age);
setTimeout(() => {
    console.log(age);
    import('./b.js').then(({ age }) => {
        console.log(age);
    })
}, 100);

// b.js
export let age = 1;

setTimeout(() => {
    age = 2;
}, 10);
// 打开 index.html
// 执行结果:
// 1
// 2
// 2
CommonJS值拷贝输出
// a.js
const b = require('./b');
console.log(b.age);
setTimeout(() => {
  console.log(b.age);
  console.log(require('./b').age);
}, 100);

// b.js
let age = 1;
setTimeout(() => {
  age = 18;
}, 10);
module.exports = {
  age
}
// 执行:node a.js
// 执行结果:
// 1
// 1
// 1
动态加载和静态编译的区别?
  • 动态加载,只有当模块运行时,才知道导出的模块key。
  • 静态编译,在编译阶段就知道导出的模块key。
// 动态加载
var test = 'hello'
module.exports = {
  [test + '1']: 'world'
}

// ----------------------
// 静态编译
export function hello() {return 'world'}
ESM的特性
  • import命令会被 JavaScript 引擎静态分析,优先于模块内的其他内容执行。
  • export命令会有变量声明提前的效果。

总结

  • CommonJS同步加载
  • AMD异步加载
  • UMD = CommonJS + AMD
  • ESM是标准规范, 取代UMD

参考

https://mp.weixin.qq.com/s/uZASCbdLrL8tWBa31CgTjA https://github.com/rollup/rollup/issues/3011#issuecomment-516084596 https://github.com/rollup/plugins/tree/master/packages/commonjs https://www.zhihu.com/question/63240671 https://www.yuque.com/baichuan/notes/emputh https://github.com/indutny/webpack-common-shake#limitations http://xbhong.top/2018/03/12/commonjs/ https://www.douban.com/note/283566440/ https://blog.fundebug.com/2018/08/15/reduce-js-payload-with-tree-shaking/ http://huangxuan.me/js-module-7day/#/13 https://www.jianshu.com/p/6c26fb7541f1

References