Node.js 如何处理 ES6 模块

目录

一、两种模块的差异

[二、Node.js 的区分](#二、Node.js 的区分)

[三、CommonJS 模块加载 ES6 模块](#三、CommonJS 模块加载 ES6 模块)

[四、ES6 模块加载 CommonJS 模块](#四、ES6 模块加载 CommonJS 模块)

五、同时支持两种格式的模块


学习 JavaScript 语言,你会发现它有两种格式的模块。

一种是 ES6 模块,简称 ESM;另一种是 Node.js 专用的 CommonJS 模块,简称 CJS。这两种模块不兼容。

很多人使用 Node.js,只会用require()加载模块,遇到 ES6 模块就不知道该怎么办。本文就来谈谈,ES6 模块在 Node.js 里面怎么使用。

一、两种模块的差异

ES6 模块和 CommonJS 模块有很大的差异。

语法上面,CommonJS 模块使用require()加载和module.exports输出,ES6 模块使用importexport

用法上面,require()是同步加载,后面的代码必须等待这个命令执行完,才会执行。import命令则是异步加载,或者更准确地说,ES6 模块有一个独立的静态解析阶段,依赖关系的分析是在那个阶段完成的,最底层的模块第一个执行。

二、Node.js 的区分

Node.js 要求 ES6 模块采用.mjs后缀文件名。也就是说,只要脚本文件里面使用import或者export命令,那么就必须采用.mjs后缀名。Node.js 遇到.mjs文件,就认为它是 ES6 模块,默认启用严格模式,不必在每个模块文件顶部指定"use strict"

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

复制代码
{
   "type": "module"
}

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

复制代码
# 解释成 ES6 模块
$ node my-app.js

如果这时还要使用 CommonJS 模块,那么需要将 CommonJS 脚本的后缀名都改成.cjs。如果没有type字段,或者type字段为commonjs,则.js脚本会被解释成 CommonJS 模块。

总结为一句话:.mjs文件总是以 ES6 模块加载,.cjs文件总是以 CommonJS 模块加载,.js文件的加载取决于package.json里面type字段的设置。

注意,ES6 模块与 CommonJS 模块尽量不要混用。require命令不能加载.mjs文件,会报错,只有import命令才可以加载.mjs文件。反过来,.mjs文件里面也不能使用require命令,必须使用import

三、CommonJS 模块加载 ES6 模块

ES6

复制代码
export const add = function (a, b) {
  return a + b;
};
export const minus = function (x, y) {
  return x - y;
};

export default {
  age: 18,
};

CommonJS

复制代码
(async function () {
  let { add, minus, default: def } = await import("./my-app.mjs");
  console.log(add(1, 2));
  console.log(minus(1, 2));
  console.log(def.age);
})();

上面代码可以在 CommonJS 模块中运行。

require()不支持 ES6 模块的一个原因是,它是同步加载,而 ES6 模块内部可以使用顶层await命令,导致无法被同步加载。

四、ES6 模块加载 CommonJS 模块

为了更好地与 JS 生态系统中的现有用法兼容,Node.js 除了*默认导入之外,*还会尝试确定每个导入的 CommonJS 模块的 CommonJS 命名导出,并使用静态分析过程将它们作为单独的 ES 模块导出提供。

CommonJS

复制代码
exports.add = function (a, b) {
  return a + b;
};
exports.minus = function (x, y) {
  return x - y;
};

ES6

复制代码
import { add, minus } from "./commonjs-package.js";
console.log(add(1, 2));
console.log(minus(1, 2));

命名导出的检测基于常见的语法模式,但并非总能正确识别, CJS 的导出是动态计算出来的,静态分析会失败。

复制代码
const add = function (a, b) {
  return a + b;
};
const minus = function (x, y) {
  return x - y;
};

module.exports = Object.assign(
  {
    age: 18,
  },
  {
    add,
    minus,
  },
);

在这种情况下,使用下述默认导入方式可能更合适。

复制代码
import pkg from "./commonjs-package.js";
console.log(pkg.add(1, 2));
console.log(pkg.minus(1, 2));
console.log(pkg.age);

命名导出检测涵盖了许多常见的导出模式、重新导出模式以及构建工具和转译器的输出。cjs-module-lexer 有关具体实现的语义,请参阅相关文档。

  1. 默认加载(Default Import): 始终有效。import cjs from './main.js' 得到的 cjs 等同于 module.exports
  2. 解构加载(Named Import): * 支持: 如果 CJS 模块使用了简单的赋值方式(如 module.exports = { a, b }exports.a = 1),Node.js 会自动解析出命名导出,允许 import { a } from './cjs.js'
  3. 不支持: 如果 CJS 的导出是动态计算 出来的(例如 module.exports = Object.assign({}, someData)),静态分析会失败。此时只能使用整体加载,再从对象中获取属性。

五、同时支持两种格式的模块

一个模块同时要支持 CommonJS 和 ES6 两种格式,也很容易。

如果原始模块是 ES6 格式,那么CommonJS 可以用 await import()进行加载。

ES6 模块 「原始模块」

复制代码
export const add = function (a, b) {
  return a + b;
};
export const minus = function (x, y) {
  return x - y;
};

CommonJS 模块

复制代码
(async function () {
  let { add, minus } = await import("./my-app.mjs");
  console.log(add(1, 2));
  console.log(minus(1, 2));
})();

如果原始模块是 CommonJS 格式,使用默认导入方式

CommonJS 模块 「原始模块」

复制代码
exports.add = function (a, b) {
  return a + b;
};
exports.minus = function (x, y) {
  return x - y;
};

ES6 模块 「原始模块」

复制代码
import pkg from "./commonjs-package.js";
const { add, minus } = pkg;
console.log(add(1, 2));
console.log(minus(1, 2));

另一种做法是在package.json文件的exports字段,指明两种格式模块各自的加载入

复制代码
"exports":{ 
    "require": "./index.js",
    "import": "./esm/wrapper.js" 
}

上面代码指定require()import,加载该模块会自动切换到不一样的入口文件。

相关推荐
虚拟世界AI1 天前
Vue.js安装指南:快速搭建开发环境
vue.js·npm·node.js
yuhaiqiang1 天前
为什么这道初中数学题击溃了所有 AI
前端·后端·面试
djk88881 天前
支持手机屏幕的layui后台html模板
前端·html·layui
紫_龙1 天前
最新版vue3+TypeScript开发入门到实战教程之watch详解
前端·javascript·typescript
默默学前端1 天前
ES6模板语法与字符串处理详解
前端·ecmascript·es6
lxh01131 天前
记忆函数 II 题解
前端·javascript
我不吃饼干1 天前
TypeScript 类型体操练习笔记(三)
前端·typescript
华仔啊1 天前
除了防抖和节流,还有哪些 JS 性能优化手段?
前端·javascript·vue.js
CHU7290351 天前
随时随地学新知——线上网课教学小程序前端功能详解
前端·小程序
清粥油条可乐炸鸡1 天前
motion入门教程
前端·css·react.js