<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          CommonJS 和 ES Module 終于要互相兼容了???

          共 4505字,需瀏覽 10分鐘

           ·

          2024-05-24 09:10

          在現(xiàn)代 JavaScript 開發(fā)中,ECMAScript Module 已經(jīng)逐漸成為了公認的業(yè)界標準。ESM 被引入 Node.js 以來,它的異步加載特性和模塊解析邏輯廣受大家好評。

          然而,由于歷史原因,很多既有代碼和第三方庫仍依賴于 CommonJS 模塊系統(tǒng),然而因為 ESM 的異步加載的設計,兩個模塊化方案一直是無法共存的,這也成了很多開發(fā)者的一大痛點。

          最近, joyeecheung 提交的一個關(guān)鍵的 Pull Request(https://github.com/nodejs/node/pull/51977) 來解決這個問題。

          在開始介紹前,我們先回顧一下 JavaScript 的兩大模塊化方案:CJSESM

          CJS 和 ESM 的前世今生

          JavaScript 的世界里,模塊化是構(gòu)建大型應用程序的基礎。模塊化可以幫助開發(fā)者在不影響全局命名空間的前提下管理代碼,便于功能分離、代碼復用和依賴管理。在 Node.js 和瀏覽器環(huán)境中,有兩種主流的模塊系統(tǒng):CommonJS(CJS)ECMAScript Module(ESM)

          CommonJSNode.js 原生支持的模塊系統(tǒng),起初為了滿足服務端模塊化的需求而被引入。CJS 使用 require 函數(shù)來加載模塊,用 module.exportsexports 對象將代碼暴露為模塊。CommonJS 模塊的特點是同步加載,這意味著代碼會在模塊被加載完成后立即執(zhí)行:

          // math.js
          function add(x, y{
            return x + y;
          }
          module.exports = { add };

          // app.js
          const math = require('./math.js');
          console.log(math.add(017)); // 打印出 17

          在服務器環(huán)境中,同步加載通常不是問題,因為文件大都在本地。然而,在瀏覽器環(huán)境中,同步加載可能會導致性能問題,因為它會阻塞瀏覽器的事件循環(huán),直到腳本完全下載和解析。

          ESM 是現(xiàn)代 JavaScript 的官方標準模塊系統(tǒng),也被最新版本的瀏覽器原生支持。與 CommonJS 不同,它們設計成靜態(tài)的,這意味著你不能在運行時動態(tài)地加載或創(chuàng)建模塊。ESM 使用 importexport 語句進行模塊的導入和導出,支持異步加載:

          // math.js
          export function add(x, y{
            return x + y;
          }

          // app.js
          import { add } from './math.js';
          console.log(add(017)); // 打印出17

          ESM 的設計允許瀏覽器優(yōu)化加載和解析過程,如通過 HTTP/2 進行有效的并行加載,以及進行 tree shaking 以剔除未使用的代碼,從而增強性能和效率。但是,在 Node.js 中,ESM 的異步特性與現(xiàn)有的大量 CommonJS 模塊存在不兼容問題。

          當前在 Node.js 中啟用 ESM 的方法要復雜一些,因為代表性的 .js 文件擴展名默認與 CommonJS 模塊關(guān)聯(lián)。為了解決此問題,Node.js 允許使用 .mjs 文件擴展名或在 package.json 中明確指定 "type": "module" 屬性來表示 ESM 模塊。

          由于 ESM 是在 Node.js 中提供支持的,所以我們可以 import cjs,但不可能 require(esm)。這種 ERR_REQUIRE_ESM 的挫敗感困擾著許多人,并且可能是 Node.js 生態(tài)系統(tǒng)中浪費時間的主要原因。

          如果包作者想要確保 CJSESM 用戶都可以使用他們的包,他們要么必須繼續(xù)將其模塊作為 CJS 發(fā)布,要么將 CJSESM 版本即作為雙模塊發(fā)布(這可能會導致一些問題,但現(xiàn)在這是一種非常常見的做法)。

          同時,許多轉(zhuǎn)譯器(例如 TypeScript 編譯器)仍然配置為生成 CJS 代碼作為其最終輸出。這些轉(zhuǎn)譯器的用戶使用 ESM 語法編寫代碼,但他們不一定知道他們的代碼最終由 Node.js 作為 CJS 運行。當他們的代碼使用真正的 ESM 第三方模塊(無法 require)時,他們會看到一個 ERR_REQUIRE_ESM 。這可能會非常令人困惑,因為他們可能假設他們的代碼是作為真正的 ESM 運行的。

          為啥不能兼容?

          自然地,人們可能會問:為什么 require() 就不能支持加載 ESM 呢?

          很長一段時間以來,Node.js 項目的答案總是這樣:

          使用 require 來加載 ES 模塊是不被支持的,因為 ES 模塊是異步執(zhí)行的。

          但這是一種文檔和其他交流方式有誤導作用的情況 - 也許它們只在談論在 Node.jsESM 中發(fā)生的事情,而不是 ESM 本身被設計成什么樣的。去年,當 joyeecheung 閱讀 V8 代碼來修復內(nèi)存泄漏問題時,偶然發(fā)現(xiàn) ESM 本身并不是真正設計成無條件異步的。而是設計成只在條件下異步 - 只有當代碼中包含頂級 await 時才會異步。

          那么,對 require() 至少支持不包含頂級 await 的 ESM 當然就沒毛病了。雖然一些庫可能有合理的理由使用頂級 await,但這可能并不會那么常見。

          的確,當 joyeecheung 后來在 npm 注冊表中對 Top 影響力的僅提供 ESM 支持的包進行 require(esm) 測試時,測試的約 30 個包中沒有一個包含頂級 await - 并且在 require() 中支持同步模塊可能已經(jīng)足夠解決生態(tài)系統(tǒng)中的許多頭痛問題。

          早期的探索與嘗試

          同步 ESM 的支持其實也經(jīng)歷了長期的討論、設計和試驗。早在 2019 年,Node.js 社區(qū)就開始探討如何支持 ESMCommonJS 之間的互操作性。期間,不少開發(fā)人員提交了 Pull Requests,提出不同的實現(xiàn)方案和改進措施。

          在那個時候,一個具有里程碑意義的 PR 討論集中在如何在 Node.js 中支持 .mjs 后綴的文件,以及如何實現(xiàn)一個雙模塊系統(tǒng),可以同時支持 CommonJSESM

          https://github.com/nodejs/node/pull/30891

          這個 pull request 試圖通過在加載器中循環(huán)事件來處理頂級 await,但它的處理方式是不安全的,這就是它被關(guān)閉的原因。

          在規(guī)范方面,基于語法的 ESM 同步評估的理論基礎在 2019 年已經(jīng)確定。隨著時間的推移,Node.js 中似乎發(fā)展出了一種關(guān)于 “ESM 是異步的,CJS 是同步的,所以 CJS 不能加載 ESM” 的神話,而在標準機構(gòu)中,ES 規(guī)范特別注意保證 ESM 只是有條件的異步,W3C 規(guī)范使用它確保 Service Workers 只允許同步模塊評估。如果規(guī)范中基于語法的同步性得到了更廣泛的認知,那么在 2019 年后可能會有更多的嘗試,文檔也不會像無條件地談論 ESM 是異步的。

          支持同步 require(esm)

          在去年年末,joyeecheung 發(fā)現(xiàn)根據(jù)語法,ESM 可以是同步的,而且只有 Node.js 把異步性投入到加載過程中后,于是 joyeecheungGeoffreyBooth 開始討論重新啟動同步 require(esm)

          在 2024 年 2 月底,joyeecheung 在為 CJSESM 加載器做一個類似 cache 的事情,開始再次深入研究它們時,他注意到似乎有一種更簡單的實現(xiàn)方式 - 只需放棄“使 ESM 加載器成為 Node.js 中唯一的加載器” 的想法,并為 CJS 加載器實現(xiàn)一些專用程序以支持同步 require(esm)。它使用的現(xiàn)有 ESM 加載器代碼越少,就越容易。

          所以,這就有了這個 PR

          https://github.com/nodejs/node/pull/51977

          它與 2019 年的 PR 的主要區(qū)別在于,這試圖使 require(esm) 的范圍保持小,并且只支持加載同步 ESM。事實證明,這在技術(shù)指導委員會(TSC)中根本不是一個有爭議的想法,并且沒有遭到多少爭議。

          目前這個特性仍然在 --experimental-require-module 標志下進行實驗,還有一些工作需要在它走出實驗階段之前完成。

          目前, require(esm) 僅支持顯式標記為 ESMESM - 通過 .mjs 擴展名或者對 .js 擴展名的 "type“: "module" 包字段。這已經(jīng)足夠支持在 npm 中加載僅 ESM 包的功能。它可以實現(xiàn)當 .js 文件出現(xiàn) ESM 語法且其最近的 package.json 中沒有 "type": "module" 字段時,回退到 ESM 加載,但這通常是用戶應該避免的 - ESM 語法檢測會產(chǎn)生開銷,一旦你的項目中有足夠的 ESM 模塊,你可能不希望 Node.js 浪費時間去猜測你的模塊類型。尤其是,當你可以只用一個顯式的 "type": "module" 字段在你的 package.json 中就可以節(jié)省這些開銷。

          最后

          說實話這個問題也困擾我很久了,相比很多 NPM 包開發(fā)者也都深受其害,希望這次 joyeecheung 的嘗試可以盡早走向生產(chǎn)吧!

          參考:

          • https://github.com/nodejs/node/pull/51977
          • https://joyeecheung.github.io/blog/2024/03/18/require-esm-in-node-js/

          點贊在看是最大的支持??????

          瀏覽 255
          1點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          1點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  北条麻妃在线一区二区三区 | 国产无码免费在线 | 狼友最新网址 | 大香蕉伊人黄色在线观看 | 能看毛片的网站 |