AMD,CMD和SeaJS,RequireJS的区分

2021/7/27 JavaScript

# AMD的代表require.js

AMD是指Asynchronous Module Definition,异步的模块加载机制。是在推广require.js时对模块规范化产出。

  1. AMD推崇的是依赖前置。
  2. AMD对加载的模块是提前读取并加载。主逻辑一定在所有依赖加载完成后才执行。

主要用于解决下述两个问题

  1. 多个文件有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器。
  2. 加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应的时间越长。

语法:

  • requireJS定义了一个函数define,它是全局变量,用来定义模块:define(id ? dependencies ?, factory)
  • 在页面上使用模块加载函数:require([dependencies], factory)
// myModule.js
//定义模块
define(['dependency'], function () {
  var name = 'Byron';
  function printName() {
    console.log(name);
  }
  return {
    printName: printName
  };
});

//加载模块
require(['myModule'], function (my) {
  my.printName();
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# CommonJS的代表node.js

CommonJS:开始于服务器端的模块化,同步定义的模块化,每个模块都是一个单独的作用域,模块输出,modules.exports,模块加载require()引入模块。

# CMD的代表sea.js

CMD是指Common Module Definition,异步的模块加载机制。是在推广sea.js时对模块规范化产出。CMD更像是AMDCommonJS的升级版,结合了两者的优点。

  • CommonJS可以做到当需要这个模块时,再读取并加载。
  • AMD可以做到避免CommonJS的“临时读取并加载文件”,它是提前读取并加载。

CMD可以做到的是,“提前读取文件,但在需要再加载”,这样可以避免浏览器临时加载文件的假死,也可以避免提前加载引起的逻辑问题。

  1. CMD推崇的是就近依赖。
  2. CMD对加载的模块是提前读取并不加载,而是在需要时加载。

# 区别

  1. CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的。
  2. AMD在加载模块完成后就会执行该模块,所有模块都加载执行完后会进入require的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行。

# ES6模块与CommonJS模块的差异

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块

# module.exports与exports,export与export default的区别

# CommonJS规范

参考地址:module.exports与exports,export与export default的区别 (opens new window)

CommonJS模块规范和ES6模块规范完全是两种不同的概念。

CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令:

var exports = module.exports;
1

于是我们可以直接在exports对象上添加方法,表示对外输出的接口,如同在module.exports上添加一样。

WARNING

注意,因为 Node 模块是通过module.exports导出的,如果直接将exports变量指向一个值,就切断了exportsmodule.exports的联系,导致意外发生:

// a.js
exports = function a() {};

// b.js
const a = require('./a.js') // a 是一个空对象
1
2
3
4
5

# ESModule

ES6使用exportimport来导出、导入模块:

// aaa.js
export const a = 1;
export const b = 2;

// bbb.js
// 全部引入
// import aaa from './aaa.js';
// 报错 export 'default' (imported as 'aaa') was not fount in './aaa.js'

import * as aaa from './aaa.js';
// 这样是可以的
// Module {
//   a: 1,
//   b: 2,
//   Symbol(Symbol.toStringTag): 'Module'
// }

// 使用解构引入也可以
import { a, b } from './aaa.js';
// a = 1; b = 2;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

使用export default,一个模块只能存在一个export default

// aaa.js
const data = {
  a: 1,
  b: 2,
}
export default data;

// bbb.js
// 全部引入
import aaa from './aaa.js';
// aaa的值为
// {
//   a: 1,
//   b: 2,
// }

import * as aaa from './aaa.js';
// Module {
//   default: { a: 1, b: 2 },
//   Symbol(Symbol.toStringTag): 'Module'
// }

// 此时不能使用解构
import { a, b } from './aaa.js';
// a/b is not found in aaa.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

同时引入默认导出与解构

// aaa.js
const data = {
  a: 1,
  b: 2,
}
export default data;
export const c = 3;

// bbb.js
import aaa, { c } from './aaa.js';
// aaa = {a:1,b:2} c=3
1
2
3
4
5
6
7
8
9
10
11

TIPS

element-ui中在index.js中写的export default导出的所有组件,使用的时候却可以使用解构

import { Button, Select } from 'element-ui';
1

使用者引用element时拿到的代码是打包过后的。根据package.json中的main字段lib/element-ui.common.js可知,其实使用者用的是打包后的代码。

webpack配置文件中build/webpack.common.js,有一个属性libraryExport: 'default',应该使得打包后的文件去掉了default那一层。

常见面试问题:

  1. requireJSAMD规范的实现,seaJSCMD规范的实现。
  2. AMD推崇的是前置依赖,CMD推崇的是就近依赖。

# CommonJS和ESModule的区别

特性 CommonJS ESModule
语法 module.exportsrequire exportimport
加载方式 同步加载 静态加载(浏览器中异步加载)
适用环境 主要用于Node.js 适用于浏览器和Node.js
模块缓存 支持模块缓存 支持模块缓存
Tree Shaking 不支持 支持
动态导入 支持动态导入(require 支持动态导入(import().then()
循环依赖 支持,但可能导致问题 支持,处理方式更合理

模块的导出导入:

  • CommonJS

    • 基础类型:拷贝;模块内修改,模块外不变;模块外修改,模块内不变;(互不影响)
    • 对象类型:引用;模块内修改,模块外变;模块外修改,模块内变;(互相影响)
  • ESModule

    • 基础类型:只读引用;模块内修改,模块外变;模块外不允许修改;
    • 对象类型:只读引用;模块内修改,模块外变;模块外修改,模块内变;(互相影响)
最近更新: 2025年03月06日 15:35:02