这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战
前言
之前2篇文章讲了exports与module.exports的异同和es6的export和export default指令,今天来讲讲ES6的模块与CommonJS的模块的异同。
异同
-
ES6的
import的是值的引用,它不对值缓存,而是实时去对应的模块取值;CommonJS的
require是会对基本类型缓存,但是如果你是引用类型,则是对应着同一个栈内存地址,所以如果exports的值后面有改,require也会实时变化。因为它
require后会在内存中生成一个对象,里面有个属性exports, 这个就是导出的值就存在这里。 类似如下:{ ... // 输出值 exports: { ... }, ... }如果多次
require这个模块,就会从这个对象的exports直接返回。如果你多次
import同一个模块,只会执行一次。下面通过例子来看看
ES6代码:
// test.mjs export let name = '答案cp3' setTimeout(() => { name = 'test' }, 500); // index.mjs import { name } from './test.mjs' console.log(name) // 答案cp3 setTimeout(() => { console.log(name) // test }, 1000);在终端上运行
node --experimental-modules index.mjs先打印了
答案cp3,然后再打印了test但是,如果你使用的是
export default,然后导出一个基本类型的变量,它的表现跟CommonJS的是一样的,因为它也是导出一个叫default的属性,值是这个变量值,所以跟export会有区别。如果是引用类型,则表现跟export一致。CommonJS代码:
// test.js let name = '答案cp3' exports.name = name setTimeout(() => { name = 'test' }, 500) // index.js let { name } = require('./test') console.log(name) // 答案cp3 setTimeout(() => { console.log(name) //答案cp3 }, 1000)两次都是打印了
答案cp3。另外,无论是ES6的
import还是CommonJS的require, 一旦导入这个变量,这个变量就是常量,不能重新赋值。当然,如果导入的变量是引用类型,可以对它的属性赋值(但是不建议这样做)。import { name } from './test.mjs' name = 'test' // 报错 let { name } = require('./test') name = 'test' // 报错最好就是
import或者require后对变量只读,不操作。 -
ES6的模块是编译时加载,CommonJS是运行时加载。
怎么理解呢?
我的理解是, 编译就是在运行之前(即编译时)就通过静态分析确定依赖关系,完成模块加载的,或者通过打包工具
webpack或者rollup等打包的过程。运行时加载就是在代码执行的时候才加载。
由于ES6编译时加载,
import,export就得需要处于顶层作用域,不然无法进行静态分析。 然后因为是编译时加载,导致import的变量会变量提升到顶部,但是不能使用表达式和变量。CommonJS运行时加载没有这些问题。
if(true) { import { name } from './test.mjs' // 报错 } import {'n' + 'name' } from './test.mjs' // 报错 console.log(name) // 正确,变量提升 import { name } from './test.mjs' -
ES6的模块是异步加载,CommonJS是同步加载。
也就是说
CommonJS如果有多个require,需要一个个排队执行,因为Node依赖的代码和模块一般是在服务器上,所以相当于本地加载,不会耗时很久。但是在浏览器就不可以,因为需要请求服务器,网络状态是不稳定的,如果是同步加载,体验就有可能会出现要等很久的情况。所以就出现了
AMD,CMD等异步加载的库,以及ES6新增的import。
总结
以上就是我总结的ES6的模块与CommonJS的模块的异同。
总结有三点:
-
ES6的
import的是值的引用(export和export default的表现不一致),CommonJS的require也要区分是基本类型还是引用类型,基本类型是会缓存,引用类型会实时取值。 -
ES6的模块是编译时加载,CommonJS是运行时加载。
-
ES6的模块是异步加载,CommonJS是同步加载。
感谢你们的阅读。