一、CommonJS和ES6的差异
在浏览器上使用的是ES6的模块规范,在node中使用的是CommonJS规范。(现在也可以使用一些第三方包在node中使用ES6的模块规范)。
import、import()、export、export default属于ES6的模块规范。
exports、module.exports、module、require属于CommonJS规范。
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
二、CommonJS模块
const {run,eat} = require('./dog.js')
CommonJS使用require引入模块的方式是动态的,所谓动态就是上面代码在被执行的时候,才会引入dog.js模块,而且引入的是完整的一个对象,并不只是run和eat两个方法。所以上面这段代码也可以和下面的代码等同。
const dog = require('./dog.js')
const run = dog.run
const eat = dog.eat
module
在CommonJS中,一个文件就是一个模块,module就表示当前模块的引用(module是一个对象)。module作为一个对象自然也就有关于当前模块信息的属性。常见的有:module.exports、module.children、module.parent等等,这里只需要关注module.exports就行。
module.exports
module.exports也是一个对象,该对象由系统创建,在外部文件引入此模块时实际就是引入了exports对象。一般我们都采用module.exports.xxx的方式导出数据,也可以使用直接给exports赋值的方式导出数据。
//导出dog.js
module.exports.eat = function(){}
//引入
const {eat} = require('./dog.js')
//直接赋值给exports进行导出
module.exports = function(){}
//引入
const eat = require('./dog.js')
exports
exports是个值得注意的地方。它的使用方法和module.exports是一样的,类似于module.exports的快捷方式。非常要注意的是不要直接给exports赋值,只能使用.对exports的属性进行赋值,如果使用=直接给exports赋值会导致数据不能导出。
//正确使用
exports.eat = function(){}//等同于module.exports.eat = funciton(){}
//无法导出
exports = 123
require()
require()用于引入模块、JSON、本地文件,这里只对引入模块做说明。其参数可以是模块名,也可以是文件路径。如果直接使用模块名,则会在node_modules中或者内置模块中进行引入。如果引入的是模块,该方法的返回值就是module.exports对象。
//导出方式
module.exports.eat = function(){}
//导入方式
const {eat} = require('./dog.js')
//导出方式
module.exports = function(){}
//导入方式
const eat = require('./dog.js')
三、ES6模块
import {run,eat} from './dog.js'
run();
ES6采用import的方式引入模块,这种方式和CommonJS正好相反,它是静态的引入模块,即在代码编译的时候就已经把run和eat方法引入了。所以在效率上会比CommonJS的require方法效率更高。
export
注意和CommonJS中exports进行区别,export在ES6中是个关键字,exports在CommonJS中是一个对象或属性。也就是说exports必须使用=对自身的属性进行赋值,而export则使用声明的方式导出变量。
//CommonJS
exports.eat = function (){}
//ES6
export function eat(){}
export用于暴露模块对外的接口。这里需要注意export暴露的是变量而不是值。注意这两者的区别,下面代码对变量和值进行了解释。
export语句必须出现在模块的顶层,如果出现在块作用域中会报错。
//报错,导出的是1,并非a,1是值,export不能直接导出值
const a = 1
export a
//正确,导出的是b
export const b =1
//正确,导出的是一个对象
export {a}
//报错
function run (){}
export run
//正确
export function run(){}
export {run}
import
import是输入接口,用来引入外部模块暴露出来的变量或者值,接口是只读的不可以被改变,如果接口是对象则可以更改对象的属性,但是不建议这样做。所有输入进来的东西我们不应该去更改它的原始值。
如果是使用export方式导出的变量就需要在import关键字后面直接接收(import {run} from './dog.js'),如果使用的是export default方式导出的值则需要在import关键字后面给值一个变量来接收(import 自定义变量名 from './dog.js')。
看到这里也许会感觉矛盾,上面说了
export只暴露变量,为什么import会接收值呢?因为有export default的存在,它的作用就是直接暴露值。其本质上也是暴露名为default的变量,但是理解为暴露的是值。
//dog.js
export function run(){}
//people.js
import {run} from 'dog.js'
//dog1.js
export default function (){}
//people1.js
import run from 'dog1.js'
export default
export default用于直接导出值,比如直接导出数值、字符串、对象、数组、方法等。在使用import引入的时候,直接给导出的值一个变量就行了。
//dog.js
export default 1
//people.js
import dog from 'dog.js'
console.log(dog) //1
import()
import()和import两者都是输入接口,区别在于前者是动态加载的,后者是静态加载的。即前者在代码执行到import()的时候才接入外部模块,而import在代码编译的时候就已经接入了。import()返回了一个promise,可以使用解构赋值的方式获取暴露的变量。
// 报错
function a (){
import run from 'dog.js'
}
//正确
function b (){
import('dog.js').then( ({run,eat}) => {
console.log(run)
})
}
粗浅了解,如果有不对的,希望可以得到指正。谢谢啦!