如何让node运行es6模块文件及其原理详解


Posted in Javascript onDecember 11, 2018

最新版的 node 支持最新版 ECMAScript 几乎所有特性,但有一个特性却一直到现在都还没有支持,那就是从 ES2015 开始定义的模块化机制。而现在我们很多项目都是用 es6 的模块化规范来写代码的,包括 node 项目,所以,node 不能运行 es6 模块文件就会很不便。

node 运行 es6 模块文件的方式有两种:

  • 转码 es6 模块为 commonjs 模块
  • hook noderequire 机制,直接让 noderequire 加载 import/export

1. 转码 es6 模块为 commonjs 模块

因为 node 支持几乎所有除 import/export 外的语法,所以我们只需要将 import/export 转码成 require/exports,而不需要转码其他语法。

比如下面的项目:

- package.json
- src/
 - index.js
 - print.js
 - ...
# package.json
{
 "main": "lib/index.js"    # 由工具转码 src 目录下源文件到 lib 目录下
}


# src/index.js
import print from './print';

print('index');

export default print;


# src/print.js
export default str => {
 console.log('print: ' + str);
};

因为 src 目录下的源文件都是 es6 模块化规范的,node 并不能直接运行,所以需要转码成 commonjs 规范的代码。

这个过程有两个方案:

  • 如果不会单独使用 src 目录下的某个文件,而仅仅是以 src/index.js 为入口文件使用,可以把 src 目录下的文件打包成一个文件到 lib/index.js:这种方式推荐使用工具 rollup
  • 如果需要单独使用 src 目录下的文件,那就需要把 src 目录下的文件一对一的转码到 lib 目录下:这种方式推荐使用工具 gulp + babel

1.1 用 rollup 把 src 目录下的文件打包成一个文件到 lib/index.js

相关文件:

# rollup.config.js
export default {
 input: 'src/index.js',
 output: {
 file: 'lib/index.js',
 format: 'cjs',
 },
};


# package.json
{
 "scripts": {
 "build": "rollup -c"
 },
 "devDependencies": {
 "rollup": "^0.66.4"
 }
}

运行命令:

npm run build

结果:

# lib/index.js
'use strict';

var print = str => {
 console.log('print: ' + str);
};

print('index');

module.exports = print;

1.2 用 gulp + babel 把 src 目录下的文件一对一的转码到 lib 目录下

相关文件:

# build.js
const gulp = require('gulp');
const babel = require('gulp-babel');

gulp.task('babel', () =>
 gulp.src('src/**/*.js')
 .pipe(babel({
  plugins: ['@babel/plugin-transform-modules-commonjs']
 }))
 .pipe(gulp.dest('lib'))
);

gulp.series('babel')();


# package.json
{
 "scripts": {
 "build": "node build.js"
 },
 "devDependencies": {
 "@babel/core": "^7.1.2",
 "@babel/plugin-transform-modules-commonjs": "^7.2.0",
 "gulp": "^4.0.0",
 "gulp-babel": "^8.0.0"
 }
}

运行命令:

npm run build

结果:

# lib/index.js
"use strict";

Object.defineProperty(exports, "__esModule", {
 value: true
});
exports.default = void 0;

var _print = _interopRequireDefault(require("./print"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

(0, _print.default)('index');
var _default = _print.default;
exports.default = _default;


# lib/print.js
"use strict";

Object.defineProperty(exports, "__esModule", {
 value: true
});
exports.default = void 0;

var _default = str => {
 console.log('print: ' + str);
};

exports.default = _default;

2. hook node 的 require 机制,直接加载 import/export

这种机制一般是通过对 noderequire 机制进行 hook,劫持 require 抓取的源文件代码,把源代码转码成 commonjs 规范之后,再传送给 require 机制原本的代码流中。

pirates 之类的第三方 npm 包提供了这种添加 hook 的功能。

babel-register 便是使用这种方式达到 node 运行 es6 模块文件的目的的。

2.1 使用 babel-register 直接运行 es6 模块文件

示例目录:

- package.json
- src/
 - entry.js       # 这里多了一个入口文件,专门用于注册 babel-register
 - index.js
 - print.js
 - ...

相关文件:

# package.json
{
 "scripts": {
 "run": "node src/entry.js"
 },
 "devDependencies": {
 "@babel/core": "^7.1.2",
 "@babel/plugin-transform-modules-commonjs": "^7.2.0",
 "@babel/register": "^7.0.0"
 }
}


# src/entry.js       # 入口文件必须使用 commonjs 规范来写,因为还没有注册 hook
require('@babel/register')({
 plugins: ['@babel/plugin-transform-modules-commonjs']
});
require('./index');


# src/index.js
import print from './print';

print('index');


# src/print.js
export default str => {
 console.log('print: ' + str);
};

运行:

npm run run

结果:

# 命令行打印

print: index

这种方式因为中间转码会有额外的性能损耗,所以不建议在生产环境下使用,只建议在开发模式下使用。

2.2 使用babel-node 直接运行 es6 模块文件

babel-node 对 babel-register 进行了封装,提供了在命令行直接运行 es6 模块文件的便捷方式。

示例目录:

- package.json
- src/
 - index.js
 - print.js
 - ...

相关文件:

# package.json
{
 "scripts": {
 "run": "babel-node src/index.js --plugins @babel/plugin-transform-modules-commonjs"
 },
 "devDependencies": {
 "@babel/core": "^7.1.2",
 "@babel/node": "^7.2.0",
 "@babel/plugin-transform-modules-commonjs": "^7.2.0"
 }
}


# src/index.js
import print from './print';

print('index');


# src/print.js
export default str => {
 console.log('print: ' + str);
};

运行:

npm run run

结果:

# 命令行打印

print: index

这种方式也不建议在生产环境下使用,只建议在开发模式下使用。

3. 链接

es6 就是指 ECMAScript 2015

es7 就是指 ECMAScript 2016

es8 就是指 ECMAScript 2017

es9 就是指 ECMAScript 2018

到写这篇文章为止,已发布了 ECMAScript 2018

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery 关键字“拖曳搜索”之“拖曳”以及 图片“提示自适应放大”效果 的实现
Apr 18 Javascript
jquery无刷新验证邮箱地址实现实例
Feb 19 Javascript
javascript中递归函数用法注意点
Jul 30 Javascript
使用JavaScript为Kindeditor自定义按钮增加Audio标签
Mar 18 Javascript
Javascript简写条件语句(推荐)
Jun 12 Javascript
基于bootstrap实现广告轮播带图片和文字效果
Jul 22 Javascript
Angular中响应式表单的三种更新值方法详析
Aug 22 Javascript
Angular2+国际化方案(ngx-translate)的示例代码
Aug 23 Javascript
vue监听键盘事件的快捷方法【推荐】
Jul 11 Javascript
跨域解决之JSONP和CORS的详细介绍
Nov 21 Javascript
vue 地图可视化 maptalks 篇实例代码详解
May 21 Javascript
vue以组件或者插件的形式实现throttle或者debounce
May 22 Javascript
jQuery实现的模仿雨滴下落动画效果
Dec 11 #jQuery
详解react阻止无效重渲染的多种方式
Dec 11 #Javascript
jQuery实现数字自动增加或者减少的动画效果示例
Dec 11 #jQuery
利用jsonp解决js读取本地json跨域的问题
Dec 11 #Javascript
实现Vue的markdown文档可以在线运行的方法示例
Dec 11 #Javascript
JS中使用new Option()实现时间联动效果
Dec 10 #Javascript
vue刷新页面时去闪烁提升用户体验效果的实现方法
Dec 10 #Javascript
You might like
全国FM电台频率大全 - 16 河南省
2020/03/11 无线电
全国FM电台频率大全 - 24 贵州省
2020/03/11 无线电
PHPCMS的使用小结
2010/09/20 PHP
PHP的一个基础知识 表单提交
2011/07/04 PHP
php强大的时间转换函数strtotime
2016/02/18 PHP
laravel如何开启跨域功能示例详解
2017/08/31 PHP
解决laravel5.4下的group by报错的问题
2019/10/16 PHP
PHP变量的作用范围实例讲解
2020/12/22 PHP
Javascript 类型转换方法
2010/10/24 Javascript
JS获取当前网址、主机地址项目根路径
2013/11/19 Javascript
JS操作iframe里的dom(实例讲解)
2014/01/29 Javascript
微信JS接口汇总及使用详解
2015/01/09 Javascript
js实现同一页面多个运动效果的方法
2015/04/10 Javascript
jQueryUI DatePicker 添加时分秒
2016/06/04 Javascript
基于JS模仿windows文件按名称排序效果
2016/06/29 Javascript
利用ES6实现单例模式及其应用详解
2017/12/09 Javascript
vue中选项卡点击切换且能滑动切换功能的实现代码
2018/11/25 Javascript
js实现鼠标点击飘爱心效果
2020/08/19 Javascript
利用Vue实现简易播放器的完整代码
2020/12/30 Vue.js
十个Python程序员易犯的错误
2015/12/15 Python
python实现弹窗祝福效果
2019/04/07 Python
实例详解python函数的对象、函数嵌套、名称空间和作用域
2019/05/31 Python
如何通过python的fabric包完成代码上传部署
2019/07/29 Python
Flask-SocketIO服务端安装及使用代码示例
2020/11/26 Python
python输出国际象棋棋盘的实例分享
2020/11/26 Python
python绘制汉诺塔
2021/03/01 Python
简单介绍CSS3中Media Query的使用
2015/07/07 HTML / CSS
canvas 下载二维码和图片加水印的方法
2018/03/21 HTML / CSS
static关键字的用法
2013/10/07 面试题
北京泡泡网网络有限公司.net面试题
2012/07/17 面试题
司法所长先进事迹
2014/06/02 职场文书
院党委组织查摆问题对照检查材料思想汇报2014
2014/10/08 职场文书
2014年物流工作总结
2014/11/25 职场文书
父亲婚礼答谢词
2015/01/04 职场文书
实例讲解Python中sys.argv[]的用法
2021/06/03 Python
python实现MD5进行文件去重的示例代码
2021/07/09 Python