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后面直接跟变量名,并且这个名字可以自由指定。

作者:邓惠子本尊

链接:https://juejin.cn/post/7033651418934444063

0 个评论

要回复文章请先登录注册