ChenPt / dailyNote

dailyNode for myself
https://github.com/ChenPt/dailyNote/issues
0 stars 0 forks source link

CommonJS 模块规范与ES Module的差异 #21

Open ChenPt opened 5 years ago

ChenPt commented 5 years ago

在ES Module还未全面支持的时候,我们写ES Module的语法,importexport需要通过babel来编译,转化成CommonJS规范。现在由于ES Module普及度已经够广了。在复习的过程中也学习到了两者的区别。

Babel转换ES Module为CommonJS 规范

// a.js
var a = 2
var b = "123"
var obj = {
  test: '12'
}
export default obj
export {
  a,
  b
} 

Babel会将其转换为

exports.default = {
  test: '12' 
}
exports.a = 2
exports.b = '123'
export._esModule = true // 有一个标记,说明该模块是由ES Module转换的

可以手动设置关闭babel对ES Module的转换,设置modules的值为false

"presets": [
    [
      "env",
      {
        "modules": false
      }
  ]

Webpack对ES Module的转换

不仅仅可以通过Babel可以转换ES Module,实际上Webpack在处理文件的时候,也会对模块语法进行一定的处理。最终使用webpack内部定义的__webpack_require__函数来引入模块,从入口文件开始引入的模块都存放到一个叫module对象的exports属性里,exports也是一个对象。 格式如下,每引入一个模块都会给exports添加一个属性,最后__webpack_require__函数返回该module.exports对象。

var module = {
  i: moduleId, // 模块ID
  l: false,
  exports: {}, // 作为结果返回.
}

两者的区别

ES Module

import 是编译阶段执行的,所以可以进行静态分析。 import语句不能够使用变量,表达式,因为编译阶段这些都还未存在。 export 导出的值其实是一个值的引用,可以理解为存放着具体值的地址。也可以理解为符号链接是一类特殊的文件, 其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用

对于ES模块导出的值,举个例子

// a.js
export var a = 1
export function caculate () {
  a++
}

// b.js
import { a, caculate } from 'a.js'

console.log(a)  // 1
caculate()
console.log(a) // 2

引入a.js的变量a 和 caculate,通过caculate修改a的值,b.js引入的变量a也发生了改变,说明了,import导入的只是一个值的引用。当该值发生变化时,会同步该变化。

CommonJS

require是在运行时加载的,只有在运行的时候才能够得到CommonJS模块导出的对象exports,可以使用变量,表达式,动态加载模块。 module.exports导出的其实是一个值的拷贝,引入模块后修改其导出的值,不会影响模块原本的值。 举个例子,改写上面的例子

var a = 1
var caculate = () => { 
  a++
}
module.exports = { a, caculate }
// b.js
var { a, caculate } = require('./a.js')

console.log(a)  // 1
caculate()
console.log(a)  // 1

但是需要记住的是,一个模块暴露提供的值,不能通过手动赋值的方式改变其的值,会报错Assignment to constant variable,只有其内部模块的方法可以修改值,这也是一种特殊的情况,其他情况下我们都可以将一个模块的暴露的值当成const常量,其引入后不可以被修改。

Node.js

现在Node对ES Module的支持还不是很理想,还在测试阶段。所以在Node环境下还是继续使用CommonJS模块规范。