CommonJs和Es Module的对比

112 阅读4分钟

前言

js早期没有模块体系,都是通过script标签引入js代码,当项目越来越大时,会出现一些问题,比如:

  • 变量污染,因为js都是顶层作用域
  • 当js文件相互依赖时,问题会更复杂,引入顺序出错时,会报很多错

为了解决以上问题,就出现了模块化,nodejs使用commonJs模块化规范;es6使用Es Module模块化规范。

CommonJs 用法

导出

可以通过 module.exports进行单个导出,多个导出,或者混合导出

common.js 文件

   // 单个导出
   let a = 1
   module.exports.a = a

   // 同时导出多个
   let a = 1
   let b = 2
   function add(val1,val2){
     a = val1
     b = val2
     return a+b
   }
   module.exports ={
    a, b, add
   }
   
   // 混合导出
   let a = 1
   let b = 2
   function add(val1,val2){
     a = val1
     b = val2
     return a+b
   }
   module.exports ={
     b, add
   }
   module.exports.a = a

导出也可以直接用exports,省略module

   let a = 1
   exports.a = a

注意

  • 导出不可以直接给 exports赋值,因为本质上导出的是module.exports,当给exports重新赋值对象,exports就会指向新的一块内存空间,它与module.exports的连接就断开了。

具体原因可查看: t.zoukankan.com/jianxian-p-…

// 这样导出的内容会是空对象
    let a = 1
    let b = 2
    exports = {
      a, b
    }

导入

通过require引入

let commons = require('./commons')
console.log(commons) // {a: 1}

动态导入

因为代码发生在运行时,所以可以动态加载代码

    let lists = ["./index.js", "./config.js"]
    lists.forEach((url) => require(url)) // 动态导入

值的改变

导出的值是值的拷贝,不可以通过add方法修改common.js文件中的a,b值,但是可以在导入文件中直接改变

let commons = require('./commons')
let result = commons.add(9,0)
console.log('index2===>',result,commons.a, commons.b) // a,b的值并没有发生变化 9 1 2
commons.a = 11 
console.log(commons.a) // 这个时候a的值就发生了变化 是 11

注意

多次加载同一个js, js文件只会加载一次, 比如下面 commons只会运行一次

// index1.js
let commons = require('./commons')

// index2.js
let commons = require('./commons')

Es Module 用法

导出

导出分为两种:exportexport default,两者可以同时进行导出,只是引入方式不同

ESmodule.js 文件

// 导出单个
  export var a = 4
  
// 导出多个
var a = 4
var b = 22
function add(val1,val2){
  a = val1
  b = val2
  return val1+val2
}
export  {
  a, b, add
}
// 或者
export default{
  a, b, add
}

导入

如果是export导出,则用在{}进行引入,引入方式如下

import {a, b, add}  from './ESmodule'
add(7,7)
console.log('index1====>', a) // index1===> 7

// 或者

import * as all from './ESmodule'
console.log('index1====>', all.a) // index1===> 4

如果是export default导出, 则通过自定义变量进行引入,引入方式如下

    import esData  from './ESmodule'
    esData.a = '111'
    esData.add(7,7)
    console.log('index1====>', esData.a) // index1===>'111'

如果既有export又有export default导出,则用如下方式导入

// 导出
export var a = 4
var b = 22
function add(val1,val2){
  a = val1
  b = val2
  return val1+val2
}
export default  {
  b, add
}
// 导入
 import esData, {a}  from './ESmodule'
 console.log(a, esData.b) // 4 22

值的改变

当是export导出

  • 导出值是值引用,并且内部有映射关系,所以 不能在导入文件index.js中直接修改值,否则会报错
  • 可以通过导出文件ESmodule.jsadd方法,修改导出文件的变量,只是这样会污染变量
   // 导出文件 ESmodule.js
      var a = 4
      var b = 22
      function add(val1,val2){
        a = val1
        b = val2
        return val1+val2
      }
      export  {
        a, b, add
      }
      
   // 导入文件
   import {a, b, add}  from './ESmodule'
   a = 4  // 不可以直接修改,会报错
   add(7,7)
  console.log('index1====>', a) // 通过add方法将ESmodule.js中的a的值改变了,这时输出7

当是export default导出

  • 导出值是浅拷贝,所以可以在导入文件index.js中直接修改值
  • 但是不能修改导出文件ESmodule.js的值
  //导出文件 ESmodule.js
     var a = 4
     var b = 22
     function add(val1,val2){
       a = val1
       b = val2
       return val1+val2
     }
     export default{
       a, b, add
     }
     
  // 导入文件 index.js
  import esData  from './ESmodule'
  esData.a = 4
  console.log('index1====>', esData.a) // 可以在导入文件中直接修改值 输出4
  add(7,7)
  console.log('index1====>', a) // 通过add方法不能将ESmodule.js中的a的值改 输出4 

exportexport default 的区别

  • 导出次数不同:export可以导出多次, export default只能导出一次
  • 导入方式不同:export在导入时要加{ },export default则不需要,并可以起任意名称
  • 值的改变不同:export只能在导出文件中改变值,export default只能在导入文件中改变值

CommonJs 和 Es Module对比

Es ModuleCommonJs
输出方式export(输出多个)
export default(输出一个)
exports(输出多个)
module.exports(输出一个)
加载可以单独加载某个方法(接口)加载整个模块,即把所有的接口都加载出来
加载时机解析阶段生成接口并对外输出运行阶段加载模块
值的变化输出的是值的引用,
原来模块的值改变,则该加载值也变
输出的值是拷贝的,
已经加载的值,会使用缓冲,
即原来模块的值改变,不会影响已经加载的该值
this指向指向undefned指向该当前模块