1. 程式人生 > >如何讓 node 運行 es6 模塊文件,及其原理

如何讓 node 運行 es6 模塊文件,及其原理

ide 打包 end module mon des print fine nat

如何讓 node 運行 es6 模塊文件,及其原理

最新版的 node 支持最新版 ECMAScript 幾乎所有特性,但有一個特性卻一直到現在都還沒有支持,那就是從 ES2015 開始定義的模塊化機制。而現在我們很多項目都是用 es6 的模塊化規範來寫代碼的,包括 node 項目,所以,node 不能運行 es6 模塊文件就會很不便。

node 運行 es6 模塊文件的方式有兩種:

  1. 轉碼 es6 模塊為 commonjs 模塊
  2. 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 規範的代碼。

這個過程有兩個方案:

  1. 如果不會單獨使用 src 目錄下的某個文件,而僅僅是以 src/index.js 為入口文件使用,可以把 src 目錄下的文件打包成一個文件到 lib/index.js:這種方式推薦使用工具 rollup
  2. 如果需要單獨使用 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 noderequire 機制,直接加載 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

後續

更多博客,查看 https://github.com/senntyou/blogs

作者:深予之 (@senntyou)

版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證)

來源:https://segmentfault.com/a/1190000017323841

如何讓 node 運行 es6 模塊文件,及其原理