如何让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。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。