【JS】Module加载

536 阅读2分钟

AMD、CMD、CommonJS和ES Module

AMD,异步模块加载机制。具体实现RequireJS,依赖前置,提前执行

//a.js
//define可以传入三个参数,分别是字符串-模块名、数组-依赖模块、函数-回调函数
define(function(){
    return 1;
})

// b.js
//数组中声明需要加载的模块,可以是模块名、js文件路径
require(['a'], function(a){
    console.log(a);// 1
});

CMD,具体实现SeaJS,依赖就近,只有到require时依赖模块才执行

CommonJS,NodeJS和Webpack的实现

  • 所有代码都运行在模块作用域,不会污染全局作用域
  • 模块是同步加载的,即只有加载完成,才能执行后面的操作
  • 模块在首次执行后就会缓存,再次加载只返回缓存结果
//a.js
module.exports = function () {
  console.log("hello world")
}

//b.js
var a = require('./a');

a();//"hello world"

//或者

//a2.js
exports.num = 1;
exports.obj = {xx: 2};

//b2.js
var a2 = require('./a2');

console.log(a2);//{ num: 1, obj: { xx: 2 } }

ES6 Module

//a.js
var name = 'lin';
var age = 13;
var job = 'ninja';

export { name, age, job};

//b.js
import { name, age, job} from './a.js';

console.log(name, age, job);// lin 13 ninja

//或者

//a2.js
export default function () {
  console.log('default ');
}

//b2.js
import customName from './a2.js';
customName(); // 'default'

浏览器加载

默认情况

浏览器是同步加载JavaScript脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间

<script>标签打开deferasync属性,脚本就会异步加载。defer是“渲染完再执行”,async是“下载完就执行”

加载ESM

<script type="module" src="./foo.js"></script>
<!-- 等同于 -->
<script type="module" src="./foo.js" defer></script>

CommonJS和ES Module的区别

  1. CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载

  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口

因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。

而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用

模块加载

Node.js v13.2 版本开始,Node.js 已经默认打开了 ES6 模块支持

.mjs文件总是以 ES6 模块加载,.cjs文件总是以 CommonJS 模块加载

.js文件的加载取决于package.json里面type字段的设置

cjs加载esm

(async () => {
  await import('./my-app.mjs');
})();

esm加载cjs

// 正确
import packageMain from 'commonjs-package';

// 报错
import { method } from 'commonjs-package';

参考

阮一峰ECMScript6中Module的加载