什么,你还不知道 CommonJS 模块与 ES 模块的区别?

CommonJS 模块与 ES 模块有三个重大的差异:

  • CommonJS 模块输出的是一个值的拷贝(浅拷贝),ES 模块输出的是值的引用。

👆 加载 CommonJS 模块会在内存里产生缓存,当再次引用该 CommonJS 模块时会直接从缓存中获取,这会导致拿到的模块不是最新的,即当被引用的模块内部改变自身导出的值时,在引用他的模块中得不到最新的值。而 ES 模块输出的是引用,所以 ES 模块会顺着该引用得到最新的值。

  • CommonJS 模块是运行时加载,ES 模块是编译时输出接口。

  • CommonJS 模块的 require() 是同步加载模块,ES 模块的 import 命令是异步加载,有一个独立的模块依赖的解析阶段。

👆 由于 CommonJS 模块的 require() 是同步加载的,这导致他不适合于浏览器,因为同步的任务会阻塞浏览器的渲染。由于服务器的资源都在本地,同步加载起来很快,因此 CommonJS 模块可以在服务器中使用。而 ES 模块的 import 命令是异步加载的,不会阻塞浏览器的渲染,因此适合在浏览器中使用,当然在服务器中使用也没问题,这样能统一浏览器和服务器模块化方案。

require 加载 CommonJS 模块时,会先在缓存中查找是否有该模块,如果在缓存中有该模块,则会直接返回缓存中的该模块,而不会走加载模块的逻辑,如果在缓存中不存在该模块,则会走真正的模块加载流程。由于 require 加载的模块存在缓存的缘故,当加载的模块内部动态修改了自身的值的时候,外部的模块得到的值并不是最新的。如下面的例子:

模块 c1 导出了变量 foo ,并且值为 bar 。500 毫秒后,将变量 foo 的值设置为 baz ,然后打印 foo 变量的值。

// c1.js

var foo = "bar";
setTimeout(() => (foo = "baz"), 500);
module.exports.foo = foo;
setTimeout(() => {
  console.log("c1 == ", foo);
}, 500);

c2 模块中使用 require 加载 c1 模块,然后输出 foo 变量,并在 1500 毫秒后重新输出 foo 。

// c2.js

var { foo } = require("./c1.js");
console.log(foo);
setTimeout(() => console.log(foo), 1500);

在终端运行 c2 模块,发现虽然在 c1 内部 foo 变量的值已经变为 baz 了,但是外部引入他的模块得到的还是旧值 bar 。

35.png

如果要得到 CommonJS 模块内最新的值,可以通过 导出函数,在该函数返回模块内部的值,然后调用该函数获取模块内部最新的值。如下面的例子:

在 c1 模块中定义值为 bar 的 foo 变量,并导出 getFoo 函数,该函数用于返回 foo 变量

// c1.js

var foo = "bar";
setTimeout(() => (foo = "baz"), 500);
// 导出函数
module.exports.getFoo = function () {
  return foo;
};

setTimeout(() => {
  console.log("c1 == ", foo);
}, 500);

c2 模块导入 getFoo 函数,并调用 getFoo 函数,输出 getFoo 函数的返回值。为了验证 getFoo 函数能否得到模块内部最新的值,延迟 1500 毫秒后再次输出调用 getFoo 函数得到的返回值。

// c2.js

var { getFoo } = require("./c1.js");
console.log(getFoo());
setTimeout(() => {
  console.log(getFoo());
}, 1500);

在终端运行 c2 模块,可以发现导出函数,通过该函数能获得模块内最新的值。

36.png

ES 模块的加载机制和 CommonJS 模块的加载机制不一样。ES 模块不会缓存模块运行结果,而是动态地去被加载的模块取值。因此当被加载的模块的值更新后,外部依赖他的模块总是能得到最新的值。如下面的例子:

m1 模块导出了变量 foo ,值为 bar ,并在 500 毫秒后将变量 foo 值设为 baz

// m1.js

export var foo = "bar";
setTimeout(() => (foo = "baz"), 500);

m2 模块导入了 m1 模块的 foo 变量,并输出 foo 变量 ,并在 500 毫秒后重新输出 foo 变量。

// m2.js

import { foo } from "./m1.js";
console.log(foo);
setTimeout(() => console.log(foo), 500);

在终端执行 m2 模块:

34.png

可以看到,m2 中可以得到 m1 模块 foo 的最新值 baz

总结

关于CommonJS 模块与 ES 模块的区别,记住下面的三句话:

  • CommonJS 模块输出的是值的拷贝(浅拷贝),ES 模块输出的是值的引用。当模块内部的值改变时,在外部,CommonJS 无法得到模块内部最新的值,而 ES 模块可以得到模块内部最新的值。

  • CommonJS 模块是运行时加载,ES 模块是编译时输出接口,ES 模块的加载效率比 CommonJS 模块的高。

  • CommonJS 模块的 require() 是同步加载模块,同步会阻塞后面的代码执行,所以 CommonJS 模块化规范适合在服务器端使用,不适合在浏览器中使用,ES 模块的 import 命令会异步加载模块,同时适合在浏览器和服务器中使用。

掘友们,加油~

原文:https://juejin.cn/post/7374238289106845735

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值