Node.js中ES6模块import与export的使用及Babel编译原理
本文详解Node.js环境下ES6模块语法import与export的使用方法及Babel编译原理。涵盖export暴露属性、三种import导入写法(具名导入、命名空间导入、默认导入)及export default的使用。深入解析Babel如何将ES6模块转换为CommonJS格式,包括__esModule标记、default属性处理及模块继承机制,帮助开发者理解Node.js对ES6模块的支持现状与兼容方案。
Node.js ES6模块概述
简述
import与export是ES6标准中定义的模块化导入与导出语法。目前Node.js环境对ES6模块的原生支持尚不完善,通常需要通过Babel工具进行编译转换,将其转化为Node.js支持的CommonJS模块化代码。关于Node.js原生模块化的具体细节,可参考其他相关技术文章。
export暴露机制
export 曝露
使用export语句可以向外暴露方法、对象、字符串等变量。具体代码示例如下:
//写法1
export var foo=function(){
console.log(1);
}
//写法2
var bar ={a:"1",b:2};
export {bar};
//写法3
var baz='hello world';
export {baz as qux};
上述代码经过Babel编译后,会转换为Node.js可执行的代码形式,如下所示:
"use strict";
//标记这个模块是es的模块
Object.defineProperty(exports, "__esModule", {
value: true
});
//写法1
var foo = exports.foo = function foo() {
console.log(1);
};
//写法2
var bar = { a: "1", b: 2 };
exports.bar = bar;
//写法3
var baz = 'hello world';
exports.qux = baz;
通过对比编译前后的代码可知,ES6中的export语句会被Babel转换为Node.js中exports对象的属性赋值方式。
import导入方式
import 导入
接下来介绍import的写法,假设我们需要引入上述定义了export的文件xx.js:
第一种写法
import {foo,qux} from './xx';
console.log(qux);
foo和qux是xx.js文件中通过export暴露出的属性。在该文件中,实际暴露的属性包括foo、bar和qux三个。由此可见,这种导入方式要求开发者明确知晓被引入文件中暴露出的属性名称,并且可以根据实际需求选择性地导入特定属性,无需枚举所有暴露的属性。
下面展示Babel编译后的代码:
'use strict';
var _ = require('./xx');
console.log(_.qux);
编译结果是一个简单的require方法调用,用于引入xx.js。这表明,使用这种具名导入方式,既可以引入ES6模块,也可以引入Node.js原生模块。
第二种写法
import * as xx from './xx';
console.log(xx.bar);
此处依然引入xx.js文件。这种写法会将xx.js中暴露出的所有属性赋值给xx这个变量(本质上相当于给module.exports起一个别名)。Babel编译后的代码如下:
'use strict';
var _ = require('./xx');
var xx = _interopRequireWildcard(_);
function _interopRequireWildcard(obj) {
//判断是node模块还是es模块
if (obj && obj.__esModule) {
return obj;
}
else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key))
newObj[key] = obj[key];
}
}
//兼容旧的babel编译
newObj.default = obj;
console.log(newObj);
return newObj;
}
}
console.log(xx.bar);
观察编译后的代码,其中包含一个判断逻辑,用于区分模块是Node模块还是ES模块。这种写法实现了对ES6模块和Node.js模块的兼容。
第三种写法
import oo from './xx'
console.log(oo.bar);
在这种写法中,oo是一个任意命名的变量。乍看之下可能与第二种写法相似,但实际有所不同。查看编译后的代码:
'use strict';
var _ = require('./xx');
var _2 = _interopRequireDefault(_);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj :
{ default: obj };
}
console.log(_2.default.bar);
在最后一行代码中,oo.bar被编译为_2.default.bar,多出了default属性。由于原模块未暴露default属性,因此直接访问bar会找不到。这说明这种导入语法并非用于引入普通export暴露的属性,而是专门用于下文将介绍的export default。
export default用法
export default 曝露且一次性曝露
关于export与export default的详细区别,将在其他文章中总结。此处仅讨论export default的用法,代码如下:
var foo=123;
export default foo;
被Babel编译后的代码:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var foo = 123;
exports.default = foo;
观察最后一行代码,变量foo会被赋值给exports.default。这正好对应了import oo from './xx'这种写法中调用default属性的机制,因此这两种用法需配合使用。既然属性值会赋给exports.default,那么还可以采用以下写法:
export default 123;
export default {foo:123};
export default function f(){}
export default function (){}
上述代码是分开书写的。需要注意的是,一个JS文件中只能使用一次export default,其原因与使用module.exports类似。除了不能写多个外,以下写法也是错误的:
export default var foo=123; //错误
export default还可以用于暴露class,此处不再赘述,核心逻辑均为暴露。
模块继承与引包
其他的一些用法
继承
这里提到的模块继承,实际上是指一个父模块引入子模块,然后将子模块暴露出的属性重新暴露出去:
export * from './xx';
被编译后的代码:
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ = require('./xx');
Object.keys(_).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _[key];
}
});
});
注意下面这句代码:
if (key === "default" || key === "__esModule") return;
default属性不向外暴露。这说明,如果我们引入的xx.js文件中使用了export default,那么上述继承写法是无效的。替代写法是:
export {default} from './xx';
这种继承写法常用于组织多个模块,经常与下文将介绍的引包操作一起使用。
引文件夹(引包)
很多人不理解下面这行代码:
import * as o from './oo'; //oo是个文件
为什么import可以引入文件夹?需要注意的是,并非任何文件夹都可以被直接导入。文件夹内部至少需要包含一个index.js文件,或者包含package.json和另一个命名的JS文件。因为当文件夹中包含index.js时,它就不再仅仅是一个文件夹,而是被视为Node.js的包(更多关于Node.js包的说明可参考相关文档)。import语句会被Babel编译成require,而require可以引用指定路径的包。因此,import可以导入一个文件夹,我们可以依赖index.js将文件夹中的其他文件代码导出,例如:

//index.js
export * from './1';
export * from './2';
我们可以通过import oo这个文件夹,获取到1.js和2.js文件中暴露出的属性。
总结
从观察Babel编译后的代码可以看出,export与exports、module.exports与export default在用法上具有相似性。具体使用哪种方式,主要取决于个人偏好。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
Node.js 安装配置与第一个 HTTP 服务器编写
2026-11-05 11:41:40
怎么使用 nvm 管理并平滑升级 Node.js 版本
2026-11-04 23:15:07
Node.js 怎么使用 zlib 模块解压 gzip 和 deflate
2026-11-03 23:40:50
直接覆盖安装还是用版本管理器?Node.js 升级方式对比
2026-11-03 22:59:07
Express.js 是什么:Node.js Web 框架核心概念与适用场景
2026-11-03 20:00:12
