ES6模块与CommonJS模块理解
CommonJS理解
CommonJS就是为JS的表现来制定规范,因为js没有模块的功能所以CommonJS应运而生,它希望js可以在任何地方运行,不只是浏览器中。通常使用在nodeJs中。 使用exports module 或 exports 进行模块导出,使用require进行模块的导入
exports 和 exports module 区别
为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令
const exports = module.exports
不能将exports和module.exports重新赋值,如果重新赋值不会生效的 如下面这几种情况,
exports = function fn (a) {console.log(a)} // 不能直接将exports变量指向一个值,因为这样等于切断了exports与module.exports的联系
// module.exports被重新赋值了, exports与module.exports联系也没有了,fn函数不会被导出
exports.fn = function (a) {console.log(a)}
module.exports = 'hello word'
正确做法应该是这样子
// common.js
let fn = function (a) {console.log(a)}
let str = 'hello word'
module.exports.str = str
exports.fn = fn // exports = module.exports 代码中可以只用一种即可
// index.js
const common = require('./common');
console.log(common) // { str: 'hello word', fn: [Function: fn] }
注意事项:package.json中不能设置type为module,否则会报错,原因是设置为module后,需要使用ES6的模块化方案
模块的缓存
第一次加载某个模块时,Node会缓存该模块。以后再加载该模块,就直接从缓存取出该模块的module.exports属性
// common.js
module.exports.a = 1
// index.js
let common = require('./common')
common.b = 2
let common1 = require('./common')
console.log(common1) // { a: 1, b: 2 }
所有缓存的模块保存在require.cache之中,如果想删除模块的缓存,可以像下面这样写。
// 删除指定模块的缓存
delete require.cache[moduleName];
// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {
delete require.cache[key];
})
总结:
- 所有代码运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序
ES6模块化理解
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量,像CommonJS模块是在运行时加载,相比较ES6模块,就会效率没那么高。
- ES6模块必须用export导出
- export 必须与模块内部的变量建立一一对应关系
如何使用ES6模块
// 首先需要建立初始化package.json文件
npm init -y
// 在package.json中设置type:"module"
"name": "practice",
"version": "1.0.0",
"main": "index.js",
"type": "module", // 这个
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
export 和 export default的区别
- export可以多次的出现,export default只能出现一次
export const str = '123'
export let fn = function (a) {console.log(a)}
let b = '456'
let c = '789'
export default b
export default c // error 报错
- export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系
// 报错
export 1;
// 报错
const m = 1;
export m;
// 正确写法
export cont a = 1
const m = 1
export {m}
- export default 就是输出一个叫做default的变量或方法,所以后面不能直接跟变量的声明语句
// 错误书写
export deafult const a = 1 // 报错
// 正确书写
let a = 1
export default a // 导入时直接为1
// 或
export default {
a //导入时为{ a: 1 }
}
// 因为直接就是导出default的变量所以可以直接跟值
expore deafult 1
import的使用
- import命令输入的变量都是只读的
// util.js
export let a = {}
// index.js
import { a } from './util.js'
a = { foo:1 } // 报错
a.foo = 1
console.log(a) // { foo: 1 }
- import命令具有提升效果
// util.js
console.log(2)
export function fn () {
console.log(3)
}
// index.js
console.log(1)
import { fn } from './util.js'
fn()
// 打印顺序 2 1 3
- import是静态执行,所以不能使用表达式和变量
- import语句是 Singleton 模式
不同的接收方式
- 接受export 导出的模块
// util.js
export const a = 1
export let fn = function (a) {console.log(a)}
// 或
const a = 1
let fn = function (a) {console.log(a)}
export { a, fn }
// index.js
// 导入模块名的名字必须与导出的名字相同
import { a, fn } from './util.js'
// 可以使用 as 来重命名
import { a as b, fn as fn1 } from './util.js'
// 可以使用 * 来把所有都出的模块合成一个对象
import * as data from './utils' // { a: 1, fn: function }
-接受export defalut 导出的模块
// util.js
const a = 1
let fn = function (a) {console.log(a)}
export default {
a,
fn
}
// index.js
// 导入时的可以随便取
import util from './util.js'
console.log(util) // { a: 1, fn: function }
-同时接受export 和 export default 导出的模块时用各自的接受方式接受即可,也可以使用* as data 把所有模块都放在data里面
ES6模块与CommonJS模块区别
- CommonJs模块输出的是一个值的拷贝,只是一个浅拷贝的过程,如果改变的是对象里面的值,还是会改变的。
// common.js
let a = 1
let fn = function () {
a++
}
module.exports.a = a
module.exports.fn = fn
// index.js
let common = require('./common')
console.log(common.a) // 1
common.fn()
console.log(common.a) // 1
- ES6模块输出的是值的引用。
// util.js
export let a = 1
export let fu = function () {
a++
}
// index.js
import { a, fn } from './util.js'
console.log(a) // 1
fn()
console.log(a) // 2
- CommonJs模块是运行时加载,ES6模块是编译时输出接口。 因为 CommonJS 加载的是一个对象(即module.exports属性)。该对象只有在脚本运行完才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态编译阶段就会生成