zl程序教程

您现在的位置是:首页 >  工具

当前栏目

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

ESNode 如何 环境 支持 架构师 module 第九篇
2023-06-13 09:15:45 时间

了解模块化

Commonjs

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

ES Module

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

痛点分析

使用 Commonjs 导出一个模块 utils

// test-cli-0174\bin\utils.js
module.exports = function () {
  console.log('hello utils');
};

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

// test-cli-0174\bin\index.js
#!/usr/bin/env node

import utils from './utils';

utils();

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

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

利用 webpack

安装 webpack

npm i -D webpack webpack-cli

修改代码

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

// test-cli-0174\bin\index.js
#!/usr/bin/env node
require('./dist/core');

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

// test-cli-0174\bin\core.js
import utils from './utils';
utils();

配置好 webpack.config.js

// 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 字段

  "scripts": {
    "build": "webpack",
    "dev": "webpack -w"
  },

执行构建

npm run build

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

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

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

npm run dev

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

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

// 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
};
// test-cli-0174\bin\utils.js
import { pathExistsSync } from 'path-exists';

export function exists(p) {
  return pathExistsSync(p);
}
// test-cli-0174\bin\core.js
import path from 'path';
import { exists } from './utils';

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

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

利用 babel 兼容低版本 node

安装依赖

npm i -D 
babel-loader
@babel/core 
@babel/preset-env 
@babel/plugin-transform-runtime 
@babel/runtime-corejs3

配置 webpack

// 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,不可混用,否则会报错。

// test-cli-0174\bin\index.mjs
#!/usr/bin/env node

import './core.mjs';
// test-cli-0174\bin\core.mjs
import path from 'path';
import { exists } from './utils.mjs';

console.log(path.resolve('.'));
console.log(exists(path.resolve('.')));
// test-cli-0174\bin\utils.js
import { pathExistsSync } from 'path-exists';

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

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

node --experimental-modules bin/index.mjs
// node14版本之后 不需要加 --experimental-modules 指令也可以
node bin/index.mjs

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

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

{
   "type": "module"
}

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