【架构师(第九篇)】如何让 Node 环境支持 ES Module

2022-12-10 13:25:40 浏览数 (1)


了解模块化

Commonjs

  • 加载模块:require
  • 导出模块:module.exports / exports.xxx

ES Module

  • 加载模块:import
  • 导出模块:export default / export xxx

痛点分析

使用 Commonjs 导出一个模块 utils

代码语言:javascript复制
// test-cli-0174binutils.js
module.exports = function () {
  console.log('hello utils');
};

主文件中通过 ES Module 的方式引入模块

代码语言:javascript复制
// test-cli-0174binindex.js
#!/usr/bin/env node

import utils from './utils';

utils();

运行程序,发现会报错 Cannot use import statement outside a module ,意思就是不让用 import 语法。

那么我们如何让 Node 环境支持 ES Module 呢?

利用 webpack

安装 webpack

代码语言:javascript复制
npm i -D webpack webpack-cli

修改代码

主文件使用 require 去调用 webpack 构建后的 core.js

代码语言:javascript复制
// test-cli-0174binindex.js
#!/usr/bin/env node
require('./dist/core');

core.js 使用 es module 引入 utils.js

代码语言:javascript复制
// test-cli-0174bincore.js
import utils from './utils';
utils();

配置好 webpack.config.js

代码语言:javascript复制
// webpack.config.js
const path = require('path');

module.exports = {
  entry: './bin/core.js',
  output: {
    path: path.join(__dirname, '/dist'),
    filename: 'core.js',
  },
  mode: 'development',
};

修改 packag.json 的 scripts 字段

代码语言:javascript复制
  "scripts": {
    "build": "webpack",
    "dev": "webpack -w"
  },

执行构建

代码语言:javascript复制
npm run build

构建完成会出现 dist 目录以及构建后的 core.js

再次运行程序,发现可以正常运行。

可以启动监听状态,当文件发生变化时,自动执行构建过程

代码语言:javascript复制
npm run dev

通过 webpack target 属性支持 Node 内置库

当我们调用 node 的内置库时,比如 path、fswebpack 构建会报错,因为 webpack 默认使用 web 环境进行构建,web 环境不存在 node 的内置库,所以我们需要修改 target 属性为 node

代码语言:javascript复制
// webpack.config.js
const path = require('path');

module.exports = {
  entry: './bin/core.js',
  output: {
    path: path.join(__dirname, '/dist'),
    filename: 'core.js',
  },
  target: 'node', // 默认是web
};
代码语言:javascript复制
// test-cli-0174binutils.js
import { pathExistsSync } from 'path-exists';

export function exists(p) {
  return pathExistsSync(p);
}
代码语言:javascript复制
// test-cli-0174bincore.js
import path from 'path';
import { exists } from './utils';

console.log(path.resolve('.'));
console.log(exists(path.resolve('.')));

执行程序,没有什么问题了。

利用 babel 兼容低版本 node

安装依赖

代码语言:javascript复制
npm i -D 
babel-loader
@babel/core 
@babel/preset-env 
@babel/plugin-transform-runtime 
@babel/runtime-corejs3

配置 webpack

代码语言:javascript复制
// webpack.config.js
const path = require('path');

module.exports = {
  entry: './bin/core.js',
  output: {
    path: path.join(__dirname, '/dist'),
    filename: 'core.js',
  },
  mode: 'development',
  target: 'node', // 默认是web
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /(node_modules|dist)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: [
              [
                '@babel/plugin-transform-runtime',
                {
                  corejs: 3,
                  regenerator: true,
                  useESModules: true,
                  helpers: true,
                },
              ],
            ],
          },
        },
      },
    ],
  },
};

node 原生如何支持 ES Module

需要修改文件名的后缀为 .mjs,并且一旦使用 mjs,所有的内容都需要使用 ES Module,不可混用,否则会报错。

代码语言:javascript复制
// test-cli-0174binindex.mjs
#!/usr/bin/env node

import './core.mjs';
代码语言:javascript复制
// test-cli-0174bincore.mjs
import path from 'path';
import { exists } from './utils.mjs';

console.log(path.resolve('.'));
console.log(exists(path.resolve('.')));
代码语言:javascript复制
// test-cli-0174binutils.js
import { pathExistsSync } from 'path-exists';

export function exists(p) {
  return pathExistsSync(p);
}

最后 通过一个指令来执行程序

代码语言:javascript复制
node --experimental-modules bin/index.mjs
// node14版本之后 不需要加 --experimental-modules 指令也可以
node bin/index.mjs

同样能得到结果,没有什么问题。

如果不希望将后缀名改成 .mjs,可以在项目的 package.json 文件中,指定 type 字段为 module

代码语言:javascript复制
{
   "type": "module"
}

一旦设置了以后,该目录里面的 JS 脚本,就被解释用 ES6 模块。

0 人点赞