共计 3903 个字符,预计需要花费 10 分钟才能阅读完成。
CommonJS 模块(上面简称 cjs) 是为 Node.js 打包 JavaScript 代码的原始办法。应用 require 和 exports(module.exports 的简写)语句定义模块。
ECMAScript 模块 (上面简称 esm) 是 ecma262 规范下封装的 JavaScript 代码重用的官网规范格局。应用 import 和 export 语句定义模块。
-cjs 只有在 node.js 环境应用。
-esm 在 node.js 和浏览器环境都能够应用
1、commonjs
在 node.js 中,每个文件都被视为一个独自的模块。模块的局部变量是公有的,只有 exports 进来的变量,能力被外界拜访。
默认状况下,node.js 会将以下情景视为 cjs 模块:
- 扩大名为.cjs 的文件;
- 扩大名为.js 的文件,且离本人最近的 package.json 文件蕴含一个顶级字段“type”,其值为“commonjs”;
- 扩大名为.js 的文件,且离本人最近的 package.json 文件不蕴含一个顶级字段“type”(倡议明确指定 type 值,而不是不定义);
- 扩展名不为.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.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 没变
导出一个对象
//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 变了
导出一个对象,多个文件,会从缓存里获取
//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 变了
如果 require 加载的是个文件夹,那么 node.js 默认会尝试加载 index.js 或者 index.node
动静导入
import() 既反对 cjs,又反对 esm
import('./lib.js').then((res)=>{//res.default})
2、es module
调用 import()时,始终应用 esm 模块加载器,
默认状况下,node.js 会将以下情景视为 esm 模块:
扩大名为 .mjs 的文件;
扩大名为.js 的文件,且离本人最近的 package.json 文件蕴含一个顶级字段“type”,其值为“module”;
1)、对于一个文件,导出的是对象的援用。如果外部的属性变动了,内部也会变动。
2)、对于再次引入同一个文件,如果文件名截然不同,则从缓存外面取。如果文件名加了 query,则从新加载文件,不会缓存里取
3)、还有种形式,要使模块屡次执行代码,请导出函数并调用该函数。
单次导入,导入根本类型
//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 没变
屡次导入,导入援用类型
//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 变了,拿的缓存里的
屡次导入,防止缓存
//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
以后文件文件模块的 url 地址。
import {readFileSync} from 'node:fs';
const buffer = readFileSync(new URL('./data.proto', import.meta.url));
import.meta.resolve 试验性能
await import.meta.resolve('./dep', import.meta.url);
const dependencyAsset = await import.meta.resolve('component-lib/asset.css');
在 esm 中应用 require
import {createRequire} from 'node:module';
const require = createRequire(import.meta.url);
// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');
留神:从 node.js v16 当前,应用 node 内置外围模块都采纳 node:xx,比方 node:fs,它和 fs 的区别是,不能被 require 缓存,而 fs 能被缓存,能够查看 v16 changeLog-issue
const path = require('node:path'); // 不被缓存
const path = require('path'); // 能够被缓存
3、esm 模块与 cjs 模块之间的差别
- esm 应用 import/export,而 cjs 应用 require/exports
- cjs 能够应用 __filename 或者 __dirname,而 esm 不行,esm 只能应用 import.meta.url
- esm 不反对本机模块。ith module.createRequire() or process.dlopen.
- cjs 应用 require.resolve,而 esm 应用 new URL(), import.meta.resolve
- cjs 能够通过环境变量指定的门路,去查找本机上对应地位的模块,而 esm 不行,
- cjs 是在运行时确定,而 esm 则在动态编译时确定。
- cjs 能够同步执行,esm 不行
共同点:模块导出的都是援用。