关于前端:CommonJS模块-和-ECMAScript模块来自官方文档

42次阅读

共计 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 不行

共同点:模块导出的都是援用。

正文完
 0