如何让node运行es6模块文件及其原理详解
最新版的node支持最新版ECMAScript几乎所有特性,但有一个特性却一直到现在都还没有支持,那就是从ES2015开始定义的模块化机制。而现在我们很多项目都是用es6的模块化规范来写代码的,包括node项目,所以,node不能运行es6模块文件就会很不便。
让node运行es6模块文件的方式有两种:
- 转码es6模块为commonjs模块
- hooknode的require机制,直接让node的require加载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 importprintfrom'./print'; print('index'); exportdefaultprint; #src/print.js exportdefaultstr=>{ 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 exportdefault{ input:'src/index.js', output:{ file:'lib/index.js', format:'cjs', }, }; #package.json { "scripts":{ "build":"rollup-c" }, "devDependencies":{ "rollup":"^0.66.4" } }
运行命令:
npmrunbuild
结果:
#lib/index.js 'usestrict'; varprint=str=>{ console.log('print:'+str); }; print('index'); module.exports=print;
1.2用gulp+babel把src目录下的文件一对一的转码到lib目录下
相关文件:
#build.js constgulp=require('gulp'); constbabel=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":"nodebuild.js" }, "devDependencies":{ "@babel/core":"^7.1.2", "@babel/plugin-transform-modules-commonjs":"^7.2.0", "gulp":"^4.0.0", "gulp-babel":"^8.0.0" } }
运行命令:
npmrunbuild
结果:
#lib/index.js "usestrict"; Object.defineProperty(exports,"__esModule",{ value:true }); exports.default=void0; var_print=_interopRequireDefault(require("./print")); function_interopRequireDefault(obj){returnobj&&obj.__esModule?obj:{default:obj};} (0,_print.default)('index'); var_default=_print.default; exports.default=_default; #lib/print.js "usestrict"; Object.defineProperty(exports,"__esModule",{ value:true }); exports.default=void0; var_default=str=>{ console.log('print:'+str); }; exports.default=_default;
2.hooknode的require机制,直接加载import/export
这种机制一般是通过对node的require机制进行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":"nodesrc/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 importprintfrom'./print'; print('index'); #src/print.js exportdefaultstr=>{ console.log('print:'+str); };
运行:
npmrunrun
结果:
#命令行打印 print:index
这种方式因为中间转码会有额外的性能损耗,所以不建议在生产环境下使用,只建议在开发模式下使用。
2.2使用babel-node直接运行es6模块文件
babel-node对babel-register进行了封装,提供了在命令行直接运行es6模块文件的便捷方式。
示例目录:
-package.json -src/ -index.js -print.js -...
相关文件:
#package.json { "scripts":{ "run":"babel-nodesrc/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 importprintfrom'./print'; print('index'); #src/print.js exportdefaultstr=>{ console.log('print:'+str); };
运行:
npmrunrun
结果:
#命令行打印 print:index
这种方式也不建议在生产环境下使用,只建议在开发模式下使用。
3.链接
es6就是指ECMAScript2015
es7就是指ECMAScript2016
es8就是指ECMAScript2017
es9就是指ECMAScript2018
到写这篇文章为止,已发布了ECMAScript2018。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。