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;
注意错误的用法:
- 不要给exports直接赋值,否则导出会失效。如下代码,对exports赋值,使其指向新的对象。module.exports却仍然是原来的空对象,因此name属性并不会被导出。
exports = {
name: 'calculater'
}
- 不恰当的把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)
注意:
- require的模块是第一次被加载,这时会首先执行该模块,然后导出内容
- require的模块曾被加载过,这时该模块的代码不会再次执行,而是直接导出上次执行后得到的结果。
- 对于不需要获取导出内容的模块,直接使用require即可。
- 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语法导入模块。
加载带有命名导出的模块
有两种方式
- 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)
- 采用整体导入的方式, 使用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有如下优势:
- 死代码监测和排除
- 模块变量和类型检查
- 编译器优化
值拷贝和动态映射
在导入一个模块时,对于CommonJs来说,获取的是一份导出值的拷贝。而在ES6 Module中则是值的动态映射,并且这个映射是只读的。