node教程,模块文件后缀选择cjs还是mjs?模块类型对比
发布于 作者:苏南大叔 来源:程序如此灵动~
nodejs内部的两大阵营,真心的令人头晕。默认是commonjs类型的,使用require加载。但是可以通过.mjs或者type:"module"来修改解释方法为es module,使用import进行加载。

苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。本文描述:类库代码如何兼容两种主流模式,既可以使用import也可以使用require。测试环境:node@16.14.2。
个人感觉,这个就是javascript世界里面新的精神内耗,纯粹的内耗。基本知识
本文是基于下面这篇文章的,可以先阅读下面这篇文章:
https://newsn.net/say/node-export-import.html
虽然默认情况下,.js文件会被解释成commonjs的。但是,可以通过package.json里面的type强制修改成为module。那么,这些.js文件就又会被强制解释为es module,虽然代码没有改变,但是却各种报错了。
所以,这里苏南大叔的建议就是:不管package.json里面的设置,commonjs的模块,就修改成.cjs。es module的模块,就修改成.mjs。如果要选择.js后缀,就要接受package.json中的type的强制设定。

被引用模块
两种写法,commonjs和es module。

commonjs,sunan.cjs:
let who = "sunan大叔";
function hola() { who = "苏南大叔"; }
module.exports = { who, hola } // 通吃,可以require也可以importes module,sunan2.mjs:
let who = "sunan大叔";
function hola() { who = "苏南大叔"; }
export default { who, hola } // 只能import模块调用
上面的两种模块,其实在.cjs或者.mjs里面都可以调用,只不过就是写法的不同罢了。
index.cjs
const sunan = require("./sunan.cjs");
sunan.hola();
console.log(sunan.who);
// const sunan2 = require("./sunan2.mjs")
(async () => {
const sunan2_ = await import("./sunan2.mjs");
const sunan2 = sunan2_.default;
sunan2.hola();
console.log(sunan2.who);
})();不能直接import,但是可以动态import。参考文章:

index.mjs
import sunan from "./sunan.cjs";
import sunan2 from "./sunan2.mjs";
sunan.hola();
console.log(sunan.who);
sunan2.hola();
console.log(sunan2.who);
题外话
关于node(前端)模块化的概念,先后出现了requirejs(AMD)/seajs(CMD)/commonjs,以及目前最流行的es module。其中,前两者AMD和CMD主要用于浏览器端的js代码模块化,目前事实上项目已死,但是,它们的思想还是一直经典流传的。如果要向它们的标准进行兼容,则代码判断标准是是否存在define函数。而commonjs目前还在大量使用中,但是已经出现了被es module所取代的趋势。
下面是一段兼容的代码,但是无关本文的commonjs或者es module,下面的这段代码,可以理解为在浏览器端做的代码兼容。
; (function (name, definition) {
var hasDefine = typeof define === 'function';
var hasExports = typeof module !== 'undefined' && module.exports;
if (hasDefine) {
// 可能使用了 requirejs或者seajs框架的浏览器环境
// AMD环境或CMD环境,amd = requirejs, cmd = seajs,目前日落西山
define(definition);
} else if (hasExports) {
// 定义为普通Node模块,commonjs 或者 esmodule 都可以对接
module.exports = definition();
} else {
// 没有使用 requirejs或者seajs框架的浏览器环境
// 将模块的执行结果挂在window变量中,在浏览器中this指向window对象,第一个参数就是用在这里的。
this[name] = definition();
}
})('hola', function () {
let who = "sunan大叔";
function hola() { who = "苏南大叔"; }
return { who, hola }
});这段代码中,可以看到,如果判断为node环境的话,就使用module.exports = definition()导出模块,也就是本文中的commonjs的导出方式,也就是说可以使用require()也可以使用import()导入模块。

结束语
在这里需要特殊说明的是:对比上一篇文章中的运行结果,本文中的所有运行结果,都没有修改who变量,输出都是原版的sunan大叔字样。
https://newsn.net/tag/node/