portrait

bill-lai

专注 WEB 端开发

nodejs模块(CommonJS)与es6模块的区别2022-02-15 10:22

CommonJS

  CommonJS是由JavaScript社区在2009提出的包含模块、文件、IO、控制台在内的一系列标准。在Node.js的实现中采用CommonJS标准的一部分,并在它的基础上进行了一些调整。我们听说的CommonJS模块和Node.js中的实现并不完全一样,现在一般谈到CommonJS其实就是Node.js的版本,而非原始版本。

模块

  CommonJS中规定每个文件就是一个文件模块,使用require函数引入使用模块。

// add.js
console.log('current file add.js')

// main.js
require('./add.js') //current file add.js

导出

  在CommonJS中导出是一个模块向外唯一暴露自身的方式。在CommonJS中通过module.exports导出模块内容。moduleCommonJS内部定义的一个对象,存放模块信息,可以看做每个模块的顶部都有一个module对象定义,这个对象有个exports属性。模块内除了module定义外还会声明exports定义,exports是指向module.exports的。所以需要注意,对exports赋值时module.exports并不会更改。

// add.js
// 模块导出可以看做
// var module = { exports: {} }
// var exports = module.exports

module.exports = {
  name: 'module.exports add'
}
// 直接更改相当于对exports变量的更改,并不会真实更改到module.exports
exports = {
  name: 'exports add'
}

// main.js
const add = require('./add.js') 
console.log(add.name)  // module.exports add

导入

  在CommonJS中使用require方法进行模块导入。require导入的模块只有第一次加载才会执行模块的内容,后续都是获取到第一次执行模块内容是生成的module.exportsrequire函数可以接受表达式,也就是动态的。

// add.js
console.log('run add.js')
module.exports = (a, b) => {
  return a + b
}

// demo1.js
const add = require('./add.js')
console.log(add(1, 2))

// demo2.js
const add = require('./add.js')
console.log(add(2, 2))

// main.js
const moduleNames = ['./demo1.js', './demo2.js']
/**
 * run add.js
 * 3
 * 4
 **/
moduleNames.forEach(require)

ES6 Modules

  在2015年6月,TC39标准委员会正式发布了ES6,从此JavaScipt语言具备模块这一特征.

模块

  ES6 Module也是将每个文件作为一个单独的模块。importexport作为保留关键字在ES6版本中也加入了进来。当使用ES6模块时会自动采用严格模式,在ES5使用use strict开启。

// add.js
console.log('current file add.js')

// main.js
import 'add.js' //current file add.js

导出

  ES6 Module使用export关键字导出模块,可以使用命名导出和默认导出两种方式。命名导出有凉中方式,第一种是将声明和导出一起执行,第二种是先声明再用同一个export导出。使用命名导出时可以使用as关键字对变量重命名。默认导出只能有一个。

// demoA.js
export const name = 'demoA'
export const add = (a, b) => a + b

// demoB.js
const name = 'demoB'
const add = (a, b) => a + b

export { name, add as addB }
export default add

导入

  ES6 Module使用import关键字导入模块。与require类似,import导入的模块只有第一次加载才会执行模块的内容。在import关键字后面使用大括号将要导入的变量名包裹起来,与导出类似可以使用as关键字对变量重命名。可以使用import * as <myModule>来讲模块的所有变量导入到<myModule>对象中。对于默认导出可以使用import后面跟变量名的方式引入,也可以使用大括号包裹,导出在default的变量中。另外导入可以多种方式并用,用逗号隔开。

// demo1.js
console.log('run demo1.js')
export const name = 'demo1'
export default (a, b) => {
  return a + b
}

// demo2
import './demo1'

export const name = 'demo2'
export default (a, b) => {
  return a - b
}

// main.js
import { name, default as add } from './demo1.js'
import diff, { name as diffName } from './demo2.js'

  ES6 Module还支持复合写法,比如将一个模块导入后马上导出,一般是专门用在模块的入口文件。

// a.js
export const add = (a, b) => a + b
export const name = 'a'
export default diff = (a, b) => a - b

// main.js
export { add, name, default as diff } from './a.js'

区别

  上面我们介绍的CommonJSES6 Module两种形式的模块我们开发中经常会使用,这里我们对比一下它们各自的特征。

动态与静态

  CommonJSES6 Module最大的区别在于前者对模块的依赖是动态的,后者是静态的。动态是指模块依赖关系的建立是在代码运行时建立的。静态是指模块依赖关系是在编译节点建立的。
  CommonJSrequire引用的路径可以动态指定,支持表达式,可以通过if语句来判断是否加载某个模块。module.exports也是个对象,可以随时动态的添加导出变量。所以在代码执行前都是没办法确定依赖关系的。ES6 Moduleimportmodule导入导出都是声明式的,不支持表达式,而且导入导出都必须在模块的顶层作用域(无法放置到if语句中)。所以ES6 Module在编译阶段就能分析出依赖关系。ES6 Module相对与CommonJS来说有下面2点优点

  • 死代码的检测和排除。什么是死代码就是指我们引入了某个库但是只使用了某些模块,其他代码并没有使用到。通过静态分析就可以在打包的时候去掉这些死代码,减少打包的体积。
  • 模块变量类型检查。ES6 Module的静态模块解构有助于确认模块之间传递的值和接口的正确性

值拷贝与动态映射

  在导入一个模块时,CommonJS是将导出值进行一份拷贝赋值给变量;而在ES6 Module中则是值的动态映射,并且这个映射是无法更改的。值拷贝意味着当引用模块内的变量变化时,引用者使用的变量可能是旧的,并且可能是错的,而动态映射则没这个问题,引用模块抛出的永远是模块内变量的映射而已。比如看下方CommonJSES6 Module的两个例子

// demo.js
var count = 0;
module.exports = {
  count: count,
  add: function(a, b) {
    count += 1
    return a + b
  }
}
// main.js
const count = require('./demo').count
const add = require('./demo').add

console.log(count) // 0
add(1, 1)
console.log(count) // 0
count += 1
console.log(count) // 拷贝值可更改
// demo.js
export let count = 0;
export const add = () => {
  count += 1
  return a + b
}

// main.js
import { count, add } from './demo'

console.log(count) // 0
add(1, 1)
console.log(count) // 1
// count += 1 SyntaxError

  完结撒花。

©2021 - bill-lai 的小站 -站点源码

本站使用ReactHookTypeScriptgithubAPISimpleMDE制作