🤔️package.json的type到底是啥意思?顺便讲讲CommonJS与EsMoudle如何混用吧

1,566 阅读3分钟

作用

在package.json中,type字段是决定了node环境执行js文件使用的模块系统。

所以type有两种值:

  1. commonjs
  2. module

即node执行文件的时候,要么使用commonjs来解释文件的导入导出,要么使用ESM模块规范

两种模块系统

commonjs

使用module.exports,或者exports来导出需要被其他模块引用的值;使用require来导入其他模块

esm

使用export,或者export default来导出需要被其他模块引用的值;使用import来导入其他模块;

小练习

下面我们一起练习一下,在不同的模块系统中运行js文件。

尝试commonjs

// package.json
{
	"type": "commonjs",
}

//add.js
exports.add = (a, b) => {
	return a + b;
};

//index.js
const { add } = require("./add");

console.log("add: ", add(1, 2));

运行结果:

成功

注: type字段默认缺省,node在没有type字段的情况下,也是用commonjs的规则运行文件

尝试esm

// package.json
{
	"type": "module",
}

//add.js
export const add = (a, b) => {
	return a + b;
};

//index.js
import { add } from "./add.js"; //这是一个小细节
console.log("add: ", add(1, 2));

运行结果:

成功

如果type的值不是module,就会报下面这个错误

其实,除了通过type去告诉node使用哪个模块系统,还有一种方式,就是通过js文件的后缀。 文件后缀为.cjs, node就会用commonjs来解析,若后缀为.mjs,node就会用esm来解析。

下面来做个实验:

// package.json
{
	"type": "commonjs",
}

//add.mjs
export const add = (a, b) => {
	return a + b;
};

//index.mjs
import { add } from "./add.js"; //这是一个小细节
console.log("add: ", add(1, 2));

这里只改变type的值,为commonjs。但js文件还是esm的语法,且文件后缀改成了.mjs

运行结果:

成功

读者也可以试试.cjs后缀的文件执行

两种模块系统的混用

在使用esm语法的文件中,导入使用commonjs导出的文件。或者倒过来。这可行吗?

前者可行的,但是后者不可行。因为import导入是异步的,require是同步的;用异步来模拟同步是可以的,但是反过来却不行。虽然commonjs中不支持import语法,但却支持import()语法,通过动态载入的方式加载其他模块。

下面我们来看个例子

// package.json
{
	"type": "commonjs",
}

//add.mjs
export const add = (a, b) => {
	return a + b;
};

//increment.cjs
exports.increment = async (num) => {
	const { add } = await import("./add.mjs");
	return add(num, 1);
};

//index.mjs
import { increment } from "./increment.cjs";
console.log("increment: ", await increment(2));

其中add模块和index模块都是esm语法,increment是commonjs。而package中的type值是commonjs

其实我们已经通过文件后缀告诉node使用哪个模块系统,type已经不起作用了

先用increment模块引用add模块,即实验commonjs模块文件引用esm模块文件,而后在index模块中引用increment,即实验esm模块文件引用commonjs模块文件。因为import()动态载入,所以需要promise,而后导致increment函数的返回结果也是一个promise;

还有一个细节需要注意,在esm模块中,支持在最顶层使用await语法,不需要async函数的包裹。而commonjs不支持

运行结果:

成功

总结:

文章的开头通过package.json中的type字段,来引出两种模块文件系统。而后讲了两种模块系统的混用。

done😊