4

CommonJS modules (hereafter referred to as cjs) are the original method of packaging JavaScript code for Node.js. Modules are defined using require and exports (short for module.exports) statements. see nodejs cjs

ECMAScript module (hereinafter referred to as esm) is the official standard format for the reuse of JavaScript code encapsulated under the ecma262 standard. Modules are defined using import and export statements. see node-esm

-cjs is only used in node.js environment.
-esm works in both node.js and browser environments

1. commonjs

In node.js, each file is treated as a separate module. The local variables of the module are private, and only the variables that are exported can be accessed by the outside world.

By default, node.js treats the following situations as cjs modules:

  • A file with the extension .cjs;
  • A file with a .js extension, and the package.json file closest to itself contains a top-level field "type" whose value is "commonjs";
  • A file with a .js extension, and the package.json file closest to itself does not contain a top-level field "type" (it is recommended to specify the type value explicitly, rather than undefined);
  • A file whose extension is not .mjs, .cjs, .json, .node, .js, and the package.json file closest to itself contains a top-level field "type" whose value is "module", but these files pass require Introduce.

When calling require(), always use the cjs module loader,
require is loaded synchronously and can be used anywhere in the code.
Due to the synchronous nature of require(), it cannot be used to load ECMAScript module files. Attempting to do so will throw an ERR_REQUIRE_ESM error. Please use import() instead.

To get the exact filename that will be loaded when require() is called, use the require.resolve() function.

1) For a file, what is exported is a reference to the object. If the properties inside change, the outside also changes.
2) If the same file is imported again, if the file name is exactly the same, it will be taken from the cache.
3), there is another way, to make the module execute the code multiple times, please export the function and call the function.

export a raw value

 //child.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};

//parent.js
let child = require('./child.js')
console.log(child.counter);  // 3
child.incCounter();
console.log(child.counter); // 3没变

export an object

 //child.js
var obj = {
    counter:3
};
function incCounter() {
    obj.counter++;
}
module.exports = {
  obj: obj,
  incCounter: incCounter,
};
//parent.js
let child = require('./child.js')
console.log(child.obj.counter);  // 3
test.incCounter();
console.log(child.obj.counter); // 4 变了

Export an object, multiple files, will be obtained from the cache

 //child.js
var obj = {
    counter:3
};
function incCounter() {
    obj.counter++;
}
module.exports = {
  obj: obj,
  incCounter: incCounter,
};
//parent.js
let child = require('./child.js')
let child1 = require('./child.js')
console.log(child.obj.counter);  // 3
test.incCounter();
console.log(child.obj.counter); // 4 变了
console.log(child1.obj.counter); // 4 变了

If require loads a folder, then node.js will try to load index.js or index.node by default

dynamic import

import() supports both cjs and esm

 import('./lib.js').then((res)=>{
    //res.default  
})

2. es module

When calling import(), always use the esm module loader,

By default, node.js treats the following situations as esm modules:
files with the extension .mjs;
A file with a .js extension, and the package.json file closest to itself contains a top-level field "type" whose value is "module";

1) For a file, what is exported is a reference to the object. If the properties inside change, the outside also changes.
2) For re-introduction of the same file, if the file name is exactly the same, it will be taken from the cache. If the file name is added with query, the file will be reloaded and will not be retrieved from the cache.
3), there is another way, to make the module execute the code multiple times, please export the function and call the function.

Single import, import basic types

 //child.mjs
var counter = 3;
function incCounter() {
  counter++;
}
export default {
  counter: counter,
  incCounter: incCounter,
};

//index.mjs
import child from "./lib.mjs";
console.log(child.counter); // 3
child.incCounter();
console.log(child.counter); // 3没变

Multiple imports, import reference types

 //child.mjs
var obj = {
  counter: 3,
};
function incCounter() {
  obj.counter++;
}
export default {
  obj: obj,
  incCounter: incCounter,
};

//index.mjs
import child from "./lib.mjs";
import child1 from "./lib.mjs";  //从缓存里面取
console.log(child.obj.counter); // 3
child.incCounter();
console.log(child.obj.counter); // 4变了

console.log(child1.obj.counter); // 4变了,拿的缓存里的

Import multiple times to avoid caching

 //child.mjs
var obj = {
  counter: 3,
};
function incCounter() {
  obj.counter++;
}
export default {
  obj: obj,
  incCounter: incCounter,
};

//index.mjs
import child from "./lib.mjs?time=1";
import child1 from "./lib.mjs?time=2";  //增加query避免缓存
console.log(child.obj.counter); // 3
child.incCounter();
console.log(child.obj.counter); // 4变了
console.log(child1.obj.counter); // 3没变,重新加载的文件

import.meta.url

The url address of the current file file module.

 import { readFileSync } from 'node:fs';
const buffer = readFileSync(new URL('./data.proto', import.meta.url));

import.meta.resolve experimental feature

 await import.meta.resolve('./dep', import.meta.url);
const dependencyAsset = await import.meta.resolve('component-lib/asset.css');

use require in esm

 import { createRequire } from 'node:module';  
const require = createRequire(import.meta.url);

// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');

Note: Since node.js v16, the use of node's built-in core modules all use node:xx, such as node:fs. The difference between it and fs is that it cannot be cached by require, while fs can be cached. You can view v16 changeLog-issue

 const path = require('node:path');  //不被缓存
const path = require('path');    //可以被缓存

3. Difference between esm module and cjs module

  • esm uses import/export, while cjs uses require/exports
  • cjs can use __filename or __dirname, process, but esm cannot, esm can only use import.meta.url
  • esm does not support native modules. But it can be loaded via module.createRequire() or process.dlopen. instead
  • cjs uses require.resolve, while esm uses new URL(), import.meta.resolve
  • cjs can find the module in the corresponding location on the machine through the path specified by the environment variable, but esm cannot.
  • cjs is determined at runtime, while esm is determined at static compile time.
  • cjs can be executed synchronously, esm can not

Common ground: all modules exported are references.

Note: Using process in esm, you can import
import process from 'node:process';


寒水寺一禅
2.3k 声望119 粉丝

人生短短急个球!