CommonJS模块(以下简称cjs)是Node.js最原始的JavaScript代码打包方式。模块是使用require和exports(module.exports的缩写)语句定义的。参见nodejscjsECMAScript模块(以下简称esm)是ecma262标准下封装的JavaScript代码复用的官方标准格式。模块是使用import和export语句定义的。请参阅nodeesm-cjs仅在node.js环境中使用。-esm在node.js和浏览器环境都可以使用1.commonjs在node.js中,每个文件都被视为一个单独的模块。模块的局部变量是私有的,只有导出的变量才能被外界访问。默认情况下,node.js会将以下情况视为cjs模块:扩展名为.cjs的文件;扩展名为.js的文件,最接近自身的package.json文件包含一个顶级字段“type”,其值为“commonjs”;文件扩展名为.js,离你最近的package.json文件不包含顶级字段“type”(建议显式指定type值而不是undefined);扩展名不是.mjs,.cjs,.json,.node,.js文件,最接近的package.json文件包含一个顶级字段“type”,其值为“module”,但这些文件是通过require导入的.调用require()时,始终使用cjs模块加载器。require是同步加载的,可以在代码的任何地方使用。由于require()的同步特性,不可能用它来加载ECMAScript模块文件。尝试这样做会引发ERR_REQUIRE_ESM错误。请改用import()。要获取调用require()时将加载的确切文件名,请使用require.resolve()函数。1)对于一个文件,导出的是对象的引用。如果内部属性发生变化,则外部属性也会发生变化。2)对于重新引入同一个文件,如果文件名完全相同,则从缓存中取出。3)另一种方法是让模块多次执行代码,请导出函数并调用函数。导出原始值//child.jsvarcounter=3;函数incCounter(){counter++;}module.exports={counter:counter,incCounter:incCounter,};//parent.jsletchild=require('./child.js')console.log(child.counter);//3child.incCounter();console.log(child.counter);//3没有改变并导出一个对象//child.jsvarobj={counter:3};functionincCounter(){obj.counter++;}module.exports={obj:obj,incCounter:incCounter,};//parent.jsletchild=require('./child.js')console.log(child.obj.柜台);//3test.incCounter();console.log(child.obj.counter);//4改成导出一个对象,多个文件,会从缓存中获取//child.jsvarobj={counter:3};functionincCounter(){obj.counter++;}module.exports={obj:obj,incCounter:incCounter,};//parent.jsletchild=require('./child.js')letchild1=require('./child.js')console.log(child.obj.counter);//3test.incCounter();console.log(child.obj.counter);//4改变了console.log(child1.obj.counter);//4changed如果require加载一个文件夹,那么node.js默认会尝试加载index.js或index.nodedynamicimportimport()同时支持cjs和esmimport('./lib.js').then((res)=>{//res.default})2.es模块调用import()时,始终使用esm模块loader,默认情况下,node.js会将以下几种情况视为esm模块:扩展名为.mjs的文件;扩展名为.js的文件,最接近的package.json文件包含一个顶级字段“type”,其值为“module”;1)对于一个文件,导出的是对象的引用。如果内部属性发生变化,则外部属性也会发生变化。2)对于重新引入同一个文件,如果文件名完全相同,则从缓存中取出。如果在文件名中加上查询,文件将被重新加载,不会从缓存中取出。3)另一种方法是让模块多次执行代码,请导出函数并调用函数。单一导入,导入基本类型//child.mjsvarcounter=3;functionincCounter(){counter++;}exportdefault{counter:counter,incCounter:incCounter,};//index.mjsimportchildfrom"./lib.mjs";console.log(child.counter);//3child.incCounter();console.log(child.counter);//3没有改变多次导入,导入引用类型//child.mjsvarobj={counter:3,};functionincCounter(){obj.counter++;}exportdefault{obj:obj,incCounter:incCounter,};//index.mjsimportchildfrom"./lib.mjs";importchild1from"./lib.mjs";//从缓存中获取console.log(child.obj.counter);//3child.incCounter();console.log(child.obj.counter);//4改变了console.log(child1.obj.counter);//4改变了,在缓存中多次导入以避免缓存//child.mjsvarobj={counter:3,};functionincCounter(){obj.counter++;}exportdefault{obj:obj,incCounter:incCounter,};//index.mjsimportchildfrom"./lib.mjs?time=1";importchild1from"./lib.mjs?time=2";//添加查询以避免缓存console.log(child.obj.counter);//3child.inc计数器();console.log(child.obj.counter);//4改变了console.log(child1.obj.counter);//3没变,重新加载的文件import.meta.url当前文件文件模块url地址import{readFileSync}from'node:fs';constbuffer=readFileSync(newURL('./data.proto',import.meta.url));import.meta.resolve实验函数awaitimport.meta。resolve('./dep',import.meta.url);constdependencyAsset=awaitimport.meta.resolve('component-lib/asset.css');在esm中使用'node:module'中的requireimport{createRequire};constrequire=createRequire(import.meta.url);//sibling-module.js是一个CommonJS模块。constsiblingModule=require('./sibling-module');注意:从node.jsv16开始,使用node内置的核心模块全部使用node:xx,比如node:fs。它和fs的区别是require不能缓存,fs可以缓存。您可以检查v16changeLog-issueconstpath=require('node:path');//不被缓存constpath=require('path');//可以缓存3.esm模块和cjs模块的区别esm使用import/export,而cjs使用require/exportscjs可以使用__filename或__dirname,process,但不能用esm,esm只能使用import.meta.urlsm不支持原生模块。但是您可以改用module.createRequire()或process.dlopen。加载cjs使用require.resolve,而esm使用newURL(),import.meta.resolvecjs可以使用环境变量指定的路径在机器modules上找到对应的位置,而esm不行,cjs是运行时确定的,而esm在静态编译时确定。cjs可以同步执行,但是esm不能有一个共同点:所有模块导出都是引用。注意:在esm中使用process时,可以importimportprocessfrom'node:process';
