我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

nodejs内部的两大阵营,真心的令人头晕。默认是commonjs类型的,使用require加载。但是可以通过.mjs或者type:"module"来修改解释方法为es module,使用import进行加载。

苏南大叔:node教程,模块文件后缀选择cjs还是mjs?模块类型对比 - node模块类型对比
node教程,模块文件后缀选择cjs还是mjs?模块类型对比(图6-1)

苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。本文描述:类库代码如何兼容两种主流模式,既可以使用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的模块,就修改成.cjses module的模块,就修改成.mjs。如果要选择.js后缀,就要接受package.json中的type的强制设定。

苏南大叔:node教程,模块文件后缀选择cjs还是mjs?模块类型对比 - package-json-type
node教程,模块文件后缀选择cjs还是mjs?模块类型对比(图6-2)

被引用模块

两种写法,commonjses module

苏南大叔:node教程,模块文件后缀选择cjs还是mjs?模块类型对比 - 模块内容对比
node教程,模块文件后缀选择cjs还是mjs?模块类型对比(图6-3)

commonjssunan.cjs

let who = "sunan大叔";
function hola() { who = "苏南大叔"; }
module.exports = { who, hola }  // 通吃,可以require也可以import

es modulesunan2.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。参考文章:

苏南大叔:node教程,模块文件后缀选择cjs还是mjs?模块类型对比 - cjs调用模块
node教程,模块文件后缀选择cjs还是mjs?模块类型对比(图6-4)

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教程,模块文件后缀选择cjs还是mjs?模块类型对比 - mjs调用模块
node教程,模块文件后缀选择cjs还是mjs?模块类型对比(图6-5)

题外话

关于node(前端)模块化的概念,先后出现了requirejs(AMD)/seajs(CMD)/commonjs,以及目前最流行的es module。其中,前两者AMDCMD主要用于浏览器端的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()导入模块。

苏南大叔:node教程,模块文件后缀选择cjs还是mjs?模块类型对比 - 浏览器环境前端模块化兼容
node教程,模块文件后缀选择cjs还是mjs?模块类型对比(图6-6)

结束语

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

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   node