CommonJS和ES6 Module究竟是什么

对于前端模块化总是稀里糊涂,今天深入学习一下前端模块化,彻底弄懂CommonJs和ES6 Module,希望本文可以给你带来帮助。

CommonJS

模块

CommonJS中规定每个文件是一个模块。将一个JS文件通过script标签插入页面与封装成CommonJS模块最大的不同在于,前者的顶层作用域是全局作用域,在进行变量及函数声明时会污染全局环境;而后者形成一个属于模块自身的作用域,所有的变量及函数只能自己访问,对外不可见。

导出

导出是一个模块向外暴露自身的唯一方式。在commonJS中,通过modul e.exports可以导出模块中的内容。下面的代码导出了一个对象,包含name和add属性。

module.exports = {
    name: 'calculater',
    add: function(a, b){
        return a+b;
    }
}
复制代码

为了书写方便,CommonJS也支持直接使用exports。

exports.name = 'calculater';
exports.add = function(a, b){
    return a+b;
}
复制代码

exports可以理解为

var module = {
    exports:{}
};
var exports = module.exports;
复制代码

注意错误的用法:

  1. 不要给exports直接赋值,否则导出会失效。如下代码,对exports赋值,使其指向新的对象。module.exports却仍然是原来的空对象,因此name属性并不会被导出。
exports = {
    name: 'calculater'
}
复制代码
  1. 不恰当的把module.exports和exports混用。如下代码,先通过exports导出add属性,然后将module.exports重新赋值为另一个对象,将导致add属性丢失,最后导出只有name。
exports.add = function(a,b){
    return a+b;
}
module.exports = {
    name: 'calculater'
}
复制代码

导入

在CommonJs中,使用require进行模块导入。

const calculator = require('./calculator.js')
let sum = calculator.add(2,3)
复制代码

注意:

  1. require的模块是第一次被加载,这时会首先执行该模块,然后导出内容
  2. require的模块曾被加载过,这时该模块的代码不会再次执行,而是直接导出上次执行后得到的结果。
  3. 对于不需要获取导出内容的模块,直接使用require即可。
  4. require函数可以接收表达式,借助这个特性可以动态地指定模块加载路径。
const moduleName = ['a.js', 'b.js'];
moduleNames.forEach(name => {
    require('./'+name)
})
复制代码

ES6 Module

模块

ES6 Module是ES语法的一部分,它也是将每个文件作为一个模块,每个模块拥有自身的作用域。

导出

在ES6 Module中使用export命令来导出模块。export有两种形式:

  • 命名导出
  • 默认导出

一个模块可以有多个命名导出,它有两种不同的写法:

//写法1,将变量的声明和导出写在一行
export const name = 'calculator'
export const add = function(a, b){return a+b}

//写法2,先进行变量的声明,然后在用同一个export语句导出。
const name = 'calculator'
const add = function(a, b){return a+b}
export {name, add}
复制代码

与命名导出不同,模块的默认导出只能有一个。

export default {
    name: 'calculator',
    add: function(a, b){
        return a+b
    }
}
复制代码

导入

ES6 Module中使用import语法导入模块。

加载带有命名导出的模块

有两种方式

  1. import后面要跟一对大括号,将导入的变量名包裹起来。并且这些变量名要与导出的变量名完全一致。
//calculator.js
const name = 'calculator'
const add = function(a, b){return a+b}
export {name, add}

//index.js
import {name, add} from './calculator.js'
add(2,3)
复制代码
  1. 采用整体导入的方式, 使用import * as myModule可以把所有导入的变量作为属性值添加到myModule中,从而减少对当前作用域的影响。
import * as calculator from './calculator.js'
console.log(calculator.add(2,3))
console.log(calculator.name)
复制代码
加载默认导出的模块

import后面直接跟变量名,并且这个名字可以自由指定

//calculator.js
export default {
    name: 'calculator',
    add: function(a, b){
        return a+b
    }
}
//index.js
import calculator from './calculator.js'
calculator.add(2,3)
复制代码
两种导入方式混合起来
import React, {Component} from 'react'
复制代码

这里的React对应的是该模块的默认导出,Component则是其命名导出中的一个变量。

CommonJS和ES6 Module的区别

动态和静态

  • CommonJS是动态的模块结构,模块依赖关系的建立发生在代码的运行阶段
  • ES Module是静态的模块结构,在编译阶段就可以分析模块的依赖关系。

相比于CommonJS,ES6 Module有如下优势:

  1. 死代码监测和排除
  2. 模块变量和类型检查
  3. 编译器优化

值拷贝和动态映射

在导入一个模块时,对于CommonJs来说,获取的是一份导出值的拷贝。而在ES6 Module中则是值的动态映射,并且这个映射是只读的。

总结

  • CommonJS使用Module.exports或exports导出
  • CommonJS使用require()函数导入,该函数返回一个对象,包含导出的变量。
  • ES6 Module使用export导出,包括命名导出或者默认导出。
  • 命名导出是export后面跟一个大括号,括号里面包含导出的变量
  • 命名导出的另一种方式是export和变量声明在一行。
  • 默认导出是export default,只能有一个默认导出
  • ES6 Module导入使用import
  • 加载带有命名导出的模块,import后面要跟一对大括号,将导入的变量名包裹起来。并且这些变量名要与导出的变量名完全一致。
  • 采用整体导入的方式, 使用import * as 可以把所有导入的变量作为属性值添加到中,从而减少对当前作用域的影响。
  • 加载默认导出的模块,import后面直接跟变量名,并且这个名字可以自由指定。

猜你喜欢

转载自juejin.im/post/7033651418934444063