99偷拍视频精品区一区二,口述久久久久久久久久久久,国产精品夫妇激情啪发布,成人永久免费网站在线观看,国产精品高清免费在线,青青草在线观看视频观看,久久久久久国产一区,天天婷婷久久18禁,日韩动漫av在线播放直播

Node的CJS與ESM有哪些不同點

今天小編給大家分享一下Node的CJS與ESM有哪些不同點的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

成都創新互聯公司-專業網站定制、快速模板網站建設、高性價比城東網站開發、企業建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式城東網站制作公司更省心,省錢,快速模板網站建設找我們,業務覆蓋城東地區。費用合理售后完善,10余年實體公司更值得信賴。

Node的CJS與ESM有哪些不同點

一、并不完美的 ESM 支持

1.1 在 Node 中使用 ESM

Node 默認只支持 CJS 語法,這意味著你書寫了一個 ESM 語法的 js 文件,將無法被執行。

如果想在 Node 中使用 ESM 語法,有兩種可行方式:

  • ⑴ 在 package.json 中新增 "type": "module" 配置項。

  • ⑵ 將希望使用 ESM 的文件改為 .mjs 后綴。

對于第一種方式,Node 會將和 package.json 文件同路徑下的模塊,全部當作 ESM 來解析。

第二種方式不需要修改 package.json,Node 會自動地把全部 xxx.mjs 文件都作為 ESM 來解析。

同理,如果在 package.json 文件中設置 "type": "commonjs",則表示該路徑下模塊以 CJS 形式來解析。 如果文件后綴名為 .cjs,Node 會自動地將其作為 CJS 模塊來解析(即使在 package.json 中配置為 ESM 模式)。

我們可以通過上述修改 package.json 的方式,來讓全部模塊都以 ESM 形式執行,然后項目上的模塊都統一使用 ESM 語法來書寫。

如果存在較多陳舊的 CJS 模塊懶得修改,也沒關系,把它們全部挪到一個文件夾,在該文件夾路徑下新增一個內容為 {"type": "commonjs"}package.json 即可。

Node 在解析某個被引用的模塊時(無論它是被 import 還是被 require),會根據被引用模塊的后綴名,或對應的 package.json 配置去解析該模塊。

1.2 ESM 引用 CJS 模塊的問題

ESM 基本可以順利地 import CJS 模塊,但對于具名的 exports(Named exports,即被整體賦值的 module.exports),只能以 default export 的形式引入:

/** @file cjs/a.js **/
// named exports
module.exports = {
    foo: () => {
        console.log("It's a foo function...")
    }
}


/** @file index_err.js **/
import { foo } from './cjs/a.js';  
// SyntaxError: Named export 'foo' not found. The requested module './cjs/a.js' is a CommonJS module, which may not support all module.exports as named exports.
foo();


/** @file index_err.js **/
import pkg from './cjs/a.js';  // 以 default export 的形式引入
pkg.foo();  // 正常執行

1.3 CJS 引用 ESM 模塊的問題

假設你在開發一個供別人使用的開源項目,且使用 ESM 的形式導出模塊,那么問題來了 —— 目前 CJS 的 require 函數無法直接引入 ESM 包,會報錯:

let { foo } = require('./esm/b.js');
              ^

Error [ERR_REQUIRE_ESM]: require() of ES Module BlogDemo3\220220\test2\esm\b.js from BlogDemo3\220220\test2\require.js not supported.
Instead change the require of b.js in BlogDemo3\220220\test2\require.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (BlogDemo3\220220\test2\require.js:4:15) {
  code: 'ERR_REQUIRE_ESM'
}

按照上述錯誤陳述,我們不能并使用 require 引入 ES 模塊(原因會在后續提及),應當改為使用 CJS 模塊內置的動態 import 方法:

import('./esm/b.js').then(({ foo }) => {
    foo();
});

// or

(async () => { 
    const { foo } = await import('./esm/b.js'); 
})();

開源項目當然不能強制要求用戶改用這種形式來引入,所以又得借助 rollup 之類的工具將項目編譯為 CJS 模塊……


由上可見目前 Node.js 對 ESM 語法的支持是有限制的,如果不借助工具處理,這些限制可能會很糟心。

對于想入門前端的新手來說,這些麻煩的規則和限制也會讓人困惑。

截至我落筆書寫本文時, Node.js LTS 版本為 16.14.0,距離開始支持 ESM 的 13.2.0 版本已過去了兩年多的時間。

那么為何 Node.js 到現在還無法打通 CJS 和 ESM?

答案并非 Node.js 敵視 ESM 標準從而遲遲不做優化,而是因為 —— CJS 和 ESM,二者真是太不一樣了。

二、CJS 和 ESM 的不同點

2.1 不同的加載邏輯

在 CJS 模塊中,require() 是一個同步接口,它會直接從磁盤(或網絡)讀取依賴模塊并立即執行對應的腳本。

ESM 標準的模塊加載器則完全不同,它讀取到腳本后不會直接執行,而是會先進入編譯階段進行模塊解析,檢查模塊上調用了 importexport 的地方,并順騰摸瓜把依賴模塊一個個異步、并行地下載下來。

在此階段 ESM 加載器不會執行任何依賴模塊代碼,只會進行語法檢錯、確定模塊的依賴關系、確定模塊輸入和輸出的變量。

最后 ESM 會進入執行階段,按順序執行各模塊腳本。

所以我們常常會說,CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口

在上方 1.2 小節,我們曾提及到 ESM 中無法通過指定依賴模塊屬性的形式引入 CJS named exports:

/** @file cjs/a.js **/
// named exports
module.exports = {
    foo: () => {
        console.log("It's a foo function...")
    }
}

/** @file index_err.js **/
import { foo } from './cjs/a.js';  
// SyntaxError: Named export 'foo' not found. The requested module './cjs/a.js' is a CommonJS module, which may not support all module.exports as named exports.
foo();

這是因為 ESM 獲取所指定的依賴模塊屬性(花括號內部的屬性),是需要在編譯階段進行靜態分析的,而 CJS 的腳本要在執行階段才能計算出它們的 named exports 的值,會導致 ESM 在編譯階段無法進行分析。

2.2 不同的模式

ESM 默認使用了嚴格模式(use strict),因此在 ES 模塊中的 this 不再指向全局對象(而是 undefined),且變量在聲明前無法使用。

這也是為何在瀏覽器中,<script> 標簽如要啟用原生引入 ES 模塊能力,必須加上 type="module" 告知瀏覽器應當把它和常規 JS 區分開來處理。

查看 ESM 嚴格模式的更多限制:

https://es6.ruanyifeng.com/#docs/module#%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F

2.3 ESM 支持“頂級 await”,但 CJS 不行。

ESM 支持頂級 await(top-level await),即 ES 模塊中,無須在 async 函數內部就能直接使用 await

// index.mjs
const { foo } = await import('./c.js');
foo();

到 Github 獲取示例代碼(test3):

https://github.com/VaJoy/BlogDemo3/tree/main/220220/test3

在 CSJ 模塊中是沒有這種能力的(即使使用了動態的 import 接口),這也是為何 require 無法加載 ESM 的原因之一。

試想一下,一個 CJS 模塊里的 require 加載器同步地加載了一個 ES 模塊,該 ES 模塊里異步地 import 了一個 CJS 模塊,該 CJS 模塊里又同步地去加載一個 ES 模塊…… 這種復雜的嵌套邏輯處理起來會變得十分棘手。

查閱關于更多“如何實現 require 加載 ESM”的討論:

https://github.com/nodejs/modules/issues/454

2.4 ESM 缺乏 __filename 和 __dirname

在 CJS 中,模塊的執行需要用函數包起來,并指定一些常用的值:

  NativeModule.wrapper = [
    '(function (exports, require, module, __filename, __dirname) { ',
    '\n});'
  ];

所以我們才可以在 CJS 模塊里直接用 __filename__dirname

而 ESM 的標準中不包含這方面的實現,即無法在 Node 的 ESM 里使用 __filename__dirname

從上方幾點可以看出,在 Node.js 中,如果要把默認的 CJS 切換到 ESM,會存在巨大的兼容性問題。

這也是 Node.js 目前,甚至未來很長一段時間,都難以解決的一場模塊規范持久戰。

如果你希望不借助工具和規則,也能放寬心地使用 ESM,可以嘗試使用 Deno 替代 Node,它默認采用了 ESM 作為模塊規范(當然生態沒有 Node 這么完善)。

三、借助工具實現 CJS、ESM 混寫

借助構建工具可以實現 CJS 模塊、ES 模塊的混用,甚至可以在同一個模塊同時混寫兩種規范的 API,讓開發不再需要關心 Node.js 上面的限制。另外構建工具還能利用 ESM 在編譯階段靜態解析的特性,實現 Tree-shaking 效果,減少冗余代碼的輸出。

這里我們以 rollup 為例,先做全局安裝:

pnpm i -g rollup

接著再安裝 rollup-plugin-commonjs 插件,該插件可以讓 rollup 支持引入 CJS 模塊(rollup 本身是不支持引入 CJS 模塊的):

pnpm i --save-dev @rollup/plugin-commonjs

我們在項目根目錄新建 rollup 配置文件 rollup.config.js

import commonjs from 'rollup-plugin-commonjs';

export default {
  input: 'index.js',  // 入口文件
  output: {
    file: 'bundle.js',  // 目標文件
    format: 'iife'
  },
  plugins: [
    commonjs({
      transformMixedEsModules: true,
      sourceMap: false,
    })
  ]
};

plugin-commonjs 默認會跳過所有含 import/export 的模塊,如果要支持如 import + require 的混合寫法,需要帶 transformMixedEsModules 屬性。

接著執行 rollup --config 指令,就能按照 rollup.config.js 進行編譯和打包了。

示例

/** @file a.js **/
export let func = () => {
    console.log("It's an a-func...");
}

export let deadCode = () => {
    console.log("[a.js deadCode] Never been called here");
}


/** @file b.js **/
// named exports
module.exports = {
    func() {
        console.log("It's a b-func...")
    },
    deadCode() {
        console.log("[b.js deadCode] Never been called here");
    }
}


/** @file c.js **/
module.exports.func = () => {
    console.log("It's a c-func...")
};

module.exports.deadCode = () => {
    console.log("[c.js deadCode] Never been called here");
}


/** @file index.js **/
let a = require('./a');
import { func as bFunc } from './b.js';
import { func as cFunc } from './c.js';

a.func();
bFunc();
cFunc();

打包后的 bundle.js 文件如下:

(function () {
	'use strict';

	function getAugmentedNamespace(n) {
		if (n.__esModule) return n;
		var a = Object.defineProperty({}, '__esModule', {value: true});
		Object.keys(n).forEach(function (k) {
			var d = Object.getOwnPropertyDescriptor(n, k);
			Object.defineProperty(a, k, d.get ? d : {
				enumerable: true,
				get: function () {
					return n[k];
				}
			});
		});
		return a;
	}

	let func$1 = () => {
	    console.log("It's an a-func...");
	};

	let deadCode = () => {
	    console.log("[a.js deadCode] Never been called here");
	};

	var a$1 = /*#__PURE__*/Object.freeze({
		__proto__: null,
		func: func$1,
		deadCode: deadCode
	});

	var require$$0 = /*@__PURE__*/getAugmentedNamespace(a$1);

	var b = {
	    func() {
	        console.log("It's a b-func...");
	    },
	    deadCode() {
	        console.log("[b.js deadCode] Never been called here");
	    }
	};

	var func = () => {
	    console.log("It's a c-func...");
	};

	let a = require$$0;

	a.func();
	b.func();
	func();

})();

可以看到,rollup 通過 Tree-shaking 移除掉了從未被調用過的 c 模塊的 deadCode 方法,但 a、b 兩模塊中的 deadCode 代碼段未被移除,這是因為我們在引用 a.js 時使用了 require,在 b.js 中使用了 CJS named exports,這些都導致了 rollup 無法利用 ESM 的特性去做靜態解析。

常規在開發項目時,還是建議盡量使用 ESM 的語法來書寫全部模塊,這樣可以最大化地利用構建工具來減少最終構建文件的體積。

以上就是“Node的CJS與ESM有哪些不同點”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注創新互聯行業資訊頻道。

當前題目:Node的CJS與ESM有哪些不同點
URL地址:http://www.yijiale78.com/article28/gihdjp.html

成都網站建設公司_創新互聯,為您提供建站公司外貿網站建設App設計網站設計品牌網站建設品牌網站設計

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

網站優化排名