使用场景不一样
require/exports是CommonJS规范,node环境使用,浏览器不支持,需要打包工具转
import/export是ES6规范,浏览器环境通过<script type='module'>
使用,node中官方不建议使用。使用的话也是babel转成CommonJS
模块机制不一样
require是运行时加载,import是编译时输出接口。
commonJS模块
因为 CommonJS 加载的是一个对象(即
module.exports
属性),该对象只有在脚本运行完才会生成。--阮一峰
commonJS中,每个模块是一个对象,module
变量表示对当前模块的引用。
module.exports
是一个module
对象的属性,表示当前模块对外输出的接口。把想要输出的对象指派给module.exports
,其他文件加载该模块,实际上就是读取module.exports
变量。
我们可以打印看一下module对象。
// module-a.js
let counter = 1
function incCounter(){
counter++
}
module.exports = {
getCounter: ()=>{
return counter
},
incCounter: incCounter,
}
console.log(module)
$ node main-commonjs.js
Module {
id: '/Users/wangsisi/code/js-code/modules/module-a.js',
path: '/Users/wangsisi/code/js-code/modules',
exports: {
getCounter: [Function: getCounter],
incCounter: [Function: incCounter]
},
filename: '/Users/wangsisi/code/js-code/modules/module-a.js',
loaded: false,
children: [],
paths: [
'/Users/wangsisi/code/js-code/modules/node_modules',
'/Users/wangsisi/code/js-code/node_modules',
'/Users/wangsisi/code/node_modules',
'/Users/wangsisi/node_modules',
'/Users/node_modules',
'/node_modules'
]
}
看一个例子
const path = './' + fileName;
const myModual = require(path);
上面的语句就是动态加载,require
到底加载哪一个模块,只有运行时才知道。import
语句做不到这一点。
ES6 模块
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。因为不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
// 报错'import' and 'export' may only appear at the top level
if (x === 2) {
import MyModual from './myModual';
}
引擎处理import
语句是在编译时,这时不会去分析或执行if
语句,所以import
语句放在if
代码块之中毫无意义,因此会报句法错误。
输出不一样
require输出的是值的拷贝
// module-a.js
let counter = 1
function incCounter(){
counter++
}
module.exports = {
counter: counter,
incCounter: incCounter,
}
// main-commonjs.js
var mod = require('./module-a')
console.log(mod.counter);
mod.incCounter();
console.log(mod.counter);
$ node main-commonjs.js
1
1
修改一下,用一个函数去取counter
// module-a.js
let counter = 1
function incCounter(){
counter++
}
module.exports = {
getCounter: ()=>{
return counter
},
incCounter: incCounter,
}
// main-commonjs.js
var mod = require('./module-a')
console.log(mod.getCounter());
mod.incCounter();
console.log(mod.getCounter());
$ node main-commonjs.js
1
2
import输出的是值的引用
ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。ES6 输入的模块变量,只是一个“符号连接”,所以这个变量是只读的,对它进行重新赋值会报错。- 阮一峰
// 在node环境下执行import要设置成.mjs文件,并且用`node --experimental-modules`启动
// main-es6.mjs
import { counter,incCounter } from './module-b'
console.log(counter); //1
incCounter();
console.log(counter); //2
// module-b.mjs
export let counter = 1
export function incCounter(){
counter++
}
$ node --experimental-modules main-es6.mjs
1
2
同步异步不一样
CommonJS 模块的require()
是同步加载模块,ES6 模块的import
命令是异步加载,有一个独立的模块依赖的解析阶段。
import虽然写在文件顶部,但是脚本执行时,在碰到这个只读变量的时候,才执行被加载的模块。