// base.js
const a = 100
module.exports = { a }
// 如果仅仅导出一个a变量,还可以这么写
module.exports = a
此时base.js这个文件就成为一个导出变量a的模块,如果要导入该模块,需要这么写:
// main.js
const { a } = require('base.js')
// or
const a = require('base.js')
导入的两种写法没有什么高深的秘密,在CommonJS的导出中,exports是挂载在全局变量module上的一个值,如果exports = a,那么被导入的就是一个值,不能解构;如果exports = { a },那么导入的就是一个包含a的对象,可以利用ES6的结构特性取出a。
ES Module
ES Module规定了使用关键词import/export来实现导入导出。具体用法:
/**
* 导出多个变量
*/
// base.js
const a = 100
export { a }
// main.js
import { a } from 'base.js'
// 导出全部
import * as base from 'base.js'
/**
* 导出多个变量
*/
// base.js
export const a = 100
export const b = 'good'
// main.js
import { a } from 'base.js'
// 导出全部
import * as base from 'base.js'
/**
* 导出一个变量
*/
// base.js
const a = 100
export default a
// main.js
import a from 'base.js'
/**
* 导出多个变量 包括default
*/
// base.js
export const a = 100
export b = () => a + 1
const c = 'good'
export default c
// main.js
import c, { a } from 'base.js'
// 导出全部
import * as base from 'base.js' // base = { a: 100, b: "[Function: b]", default: "good" }
首先export const a = 100仅仅是一个简写的语法糖,本质上仍然是在导出的对象中添加了一个a=100的属性,如果模块里仅有一个属性需要导出,可以使用default关键词。
在没有关键词default导出时,ES6结构是常用的导入方式,如果需要导入模块里所有的导出变量,需要用特殊的语法import * as base from 'base.js',这句意思是导出全部变量并且给定一个base的别名;
而import XX from 'xx'仅仅是导出xx模块中的default部分,这一点和我们直觉的认知上有一些出入。
import { a } from 'a'
import b from 'b'
console.log(a)
console.log(b)
"use strict";
var _a = require("a");
var _b = _interopRequireDefault(require("b"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
console.log(_a.a);
console.log(_b.default);
再来看导入的语法。对于import { a } from 'a',CommonJS和ES Module的处理方式是一样的,所以仅仅是变成var _a = require("a"),但是对于import b from 'b',在CommonJS中意味着要导出b模块中所有的内容到b变量下,而在ES Module中则表示把b模块中的default导出到b变量下,所以babel在此处判断了一下模块所属类型。
CommonJS vs ES Module
这两种模块规范显然是有很大区别的,上面已经涉及到一些了,这里再做一些总结:
default关键词是ES Module特有的,可以导出默认的对象,import XX from 'xx'可以导入xx模块的默认对象;
webpack的tree shaking功能,要求我们如果用到了babel-preset-env,需要阻止将ES Module代码转化为CommonJS代码,这和它们的第三点差异有很大关系。
tree shaking也是发生在静态分析阶段的,而require语句需要等到运行时才会决定是否需要引入,webpack不能确信这个模块是不需要的,也就不能删掉这个模块。
ES_Module & CommonJS
模块化规范
require
语句提升到顶部执行),实现框架:RequireJSrequire
语句语句再去执行模块内的代码)使用方法
CommonJS
CommonJs
使用关键词require
/module.exports
来实现模块的导入和导出。具体用法:此时base.js这个文件就成为一个导出变量a的模块,如果要导入该模块,需要这么写:
导入的两种写法没有什么高深的秘密,在
CommonJS
的导出中,exports
是挂载在全局变量module
上的一个值,如果exports = a
,那么被导入的就是一个值,不能解构;如果exports = { a }
,那么导入的就是一个包含a的对象,可以利用ES6的结构特性取出a。ES Module
ES Module
规定了使用关键词import/export
来实现导入导出。具体用法:首先
export const a = 100
仅仅是一个简写的语法糖,本质上仍然是在导出的对象中添加了一个a=100
的属性,如果模块里仅有一个属性需要导出,可以使用default
关键词。 在没有关键词default
导出时,ES6结构是常用的导入方式,如果需要导入模块里所有的导出变量,需要用特殊的语法import * as base from 'base.js'
,这句意思是导出全部变量并且给定一个base
的别名; 而import XX from 'xx'
仅仅是导出xx
模块中的default
部分,这一点和我们直觉的认知上有一些出入。ES Module to CommonJS
前面谈到目前还没有原生支持的
ES Module
实现,所以我们日常书写的代码模块都是转译到CommonJS处理的,这里用Babel来看看ES Module和CommonJS的对应关系。babel
配置详见官方文档首先看导出语法的转译,在CommonJS中的全局对象定义了一个标志来表示这个是ES6的模块,然后给后面要用到的属性先进行初始化(也就是赋值为
undefined
),然后依次把export
导出的变量挂载到exports
上,而对于export default
导出的值则是挂载到exports.default
上。再来看导入的语法。对于
import { a } from 'a'
,CommonJS和ES Module的处理方式是一样的,所以仅仅是变成var _a = require("a")
,但是对于import b from 'b'
,在CommonJS
中意味着要导出b模块
中所有的内容到b变量
下,而在ES Module
中则表示把b模块
中的default
导出到b变量
下,所以babel
在此处判断了一下模块所属类型。CommonJS vs ES Module
这两种模块规范显然是有很大区别的,上面已经涉及到一些了,这里再做一些总结:
default
关键词是ES Module
特有的,可以导出默认的对象,import XX from 'xx'
可以导入xx
模块的默认对象;第一点已经讲过。
第二点关于导入的属性的绑定关系,这两者有很大区别:
在CommonJS中,当导入的是基础类型时,会复制一份到当前模块,之后原模块再改变这个变量不会引起拷贝的变化,这就是值传递;如果导入的是一个对象时,则是遵循引用传递的原则,会复制一份导入对象的地址到当前模块,如果原模块直接修改这个对象的属性,在当前模块里按照地址去读取对象,会是改变后的对象,如果原模块修改这个对象,则会重新分配一个新地址储存新对象,当前模块按照原地址读取的是未改变的元对象,这就是引用传递。
在ES Module中,不管导入的是基础类型的属性还是一个对象,当导出的对象发生变化,当前模块都能追踪到变化后的值,这就是所谓的强绑定。
有关于第三点,和两种模块解析的时机有关。 CommonJS是在运行时期解析,所以可以动态导入,比如:
而ES Module要求导入语句必须在模块的顶层,之前不能有其他代码。这是因为模块解析在静态分析阶段就发生了,静态分析器不会知道
process.env.NODE_NEV === 'development'
这个条件是否成立。webpack
的tree shaking
功能,要求我们如果用到了babel-preset-env
,需要阻止将ES Module
代码转化为CommonJS
代码,这和它们的第三点差异有很大关系。tree shaking
也是发生在静态分析阶段的,而require
语句需要等到运行时才会决定是否需要引入,webpack
不能确信这个模块是不需要的,也就不能删掉这个模块。参考
Webpack: Import vs Require, and why