理解import、export、module.exports、require等

2,528 阅读4分钟

一、CommonJS和ES6的差异

在浏览器上使用的是ES6的模块规范,在node中使用的是CommonJS规范。(现在也可以使用一些第三方包在node中使用ES6的模块规范)。

importimport()exportexport default属于ES6的模块规范。

exportsmodule.exportsmodulerequire属于CommonJS规范。

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

二、CommonJS模块

const {run,eat} = require('./dog.js')

CommonJS使用require引入模块的方式是动态的,所谓动态就是上面代码在被执行的时候,才会引入dog.js模块,而且引入的是完整的一个对象,并不只是runeat两个方法。所以上面这段代码也可以和下面的代码等同。

const dog = require('./dog.js')
const run = dog.run
const eat = dog.eat

module

CommonJS中,一个文件就是一个模块module就表示当前模块的引用(module是一个对象)。module作为一个对象自然也就有关于当前模块信息的属性。常见的有:module.exportsmodule.childrenmodule.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正好相反,它是静态的引入模块,即在代码编译的时候就已经把runeat方法引入了。所以在效率上会比CommonJS的require方法效率更高。

export

注意和CommonJSexports进行区别,export在ES6中是个关键字,exportsCommonJS中是一个对象或属性。也就是说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)
    })
}

粗浅了解,如果有不对的,希望可以得到指正。谢谢啦!