亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

JavaScript 模塊化現(xiàn)狀

draveness / 2860人閱讀

摘要:比如,這種模擬并不符合某些邊緣情況。然而,使用,結(jié)果是在引導(dǎo)模塊時(shí),將指向,與使用的行為一致。這將更容易完成代碼庫的過渡,并使成為可能。

原文作者:Johannes Ewald @Jhnnns

原文鏈接:The state of JavaScript modules

已獲原作者授權(quán)翻譯及發(fā)布

ESM, CJS, UMD, AMD?—?到底應(yīng)該選擇哪一個(gè)?

最近 在 twitter 上有很多關(guān)于 ES Module 現(xiàn)狀的討論,尤其是在 Node.js 上,他們計(jì)劃引入新的文件擴(kuò)展名 *.mjs。人們有足夠理由對此感到 擔(dān)憂和不確定,因?yàn)檫@個(gè)話題異常復(fù)雜,接下來會(huì)盡力闡述清楚問題。

來自遠(yuǎn)古的恐懼

大多數(shù)前端開發(fā)者應(yīng)該還記得 Javascript 依賴管理的黑暗時(shí)期。那個(gè)時(shí)候,你需要把一個(gè)庫復(fù)制粘貼到 vendor 文件夾,然后作為一個(gè)全局變量引入,要自己去按次序組合所有東西,可能還要管理命名空間。

在過去的那些年,我們能深刻體會(huì)到公共模塊格式化和中央模塊管理的價(jià)值。

在今天,不管是發(fā)布還是使用一個(gè)庫都要容易得多,只需要使用 npm publishnpm install 命令就行。這就是人們會(huì)那么緊張兩種模塊系統(tǒng)兼容性問題的原因:他們不想失去已有的舒適區(qū)。

接下來我會(huì)解釋和總結(jié)現(xiàn)有實(shí)現(xiàn)的情況,以及為什么 Node 生態(tài)遷移到 ES Module(ESM)會(huì)那么難。在最后,總結(jié)這些變化對 webpack 使用者和模塊作者有什么影響。

現(xiàn)有實(shí)現(xiàn)

目前,ESM 有三種方式的實(shí)現(xiàn):

瀏覽器

webpack 以及類似的構(gòu)建工具

Node(未完成,但可能在年底作為一個(gè)實(shí)驗(yàn)功能)

為了更好地理解現(xiàn)在的討論,首先要知道 ES2015 包含兩種模式:

script 用于具有全局命名空間的常規(guī)腳本

module 用于具有明確導(dǎo)入和導(dǎo)出的模塊化代碼

如果你試圖在 script 標(biāo)簽使用 import 或者 export 語句,會(huì)拋出一個(gè) SyntaxError。這種語句在全局環(huán)境下沒有任何意義。另一方面,module 模式即意味著嚴(yán)格模式,禁止使用某些語言特性,比如 with 語句。因此,需要在腳本被解析和執(zhí)行之前定義模式。

瀏覽器中的 ESM

截至到 2017 年 5 月,所有主流瀏覽器都開始做了 ESM 的實(shí)現(xiàn)工作。不過,大部分仍處于在實(shí)驗(yàn)性質(zhì)。這里不會(huì)做詳細(xì)介紹,因?yàn)?Jake Archibald 已經(jīng)寫了一篇很厲害的文章。

除了一些小的困難,在瀏覽器中實(shí)現(xiàn)起來非常容易,因?yàn)橐郧安]有模塊系統(tǒng)。想要指定 module 模式,需要在 script 標(biāo)簽添加 type="module" 屬性,如下所示:

在一個(gè)模塊中,現(xiàn)在只能使用有效的 URL 作為模塊標(biāo)識(shí)符。模塊標(biāo)識(shí)符是用于 require 或 import 其他模塊的字符串。為了確保未來兼容 CJS 模塊標(biāo)識(shí)符,“純” 導(dǎo)入標(biāo)識(shí)符(如 import "lodash")現(xiàn)在還不支持。模塊標(biāo)識(shí)符必須是絕對 URL 或者是以 /, ./, ../ 開頭:

// Supported:
import {foo} from "https://jakearchibald.com/utils/bar.js";
import {foo} from "/utils/bar.js";
import {foo} from "./bar.js";
import {foo} from "../bar.js";

// Not supported:
import {foo} from "bar.js";
import {foo} from "utils/bar.js";
// Example from https://jakearchibald.com/2017/es-modules-in-browsers/

同樣需要注意的是,一旦處在一個(gè)模塊中,每個(gè)導(dǎo)入也將被解析為 module,而且沒有辦法 import 一個(gè) script。

ESM 與 webpack

類似 webpack 這樣的構(gòu)建工具通常會(huì)嘗試用 module 模式解析代碼,有問題再切回到 script 模式。這些工具最終會(huì)生成一段 script,通常是在一定程度上模擬 CJS 和 ESM 行為的模塊運(yùn)行時(shí)。

我們以這兩個(gè)簡單的 ESM 為例:

// a.js
export let number = 42;
export function incr() {
    number++;
}
// test.js
import { number } from "./a";

console.log(number);

webpack 使用函數(shù)包裝器封裝模塊范圍和對象引用來模擬 ESM 實(shí)時(shí)綁定。每次編譯,還包括一個(gè)模塊運(yùn)行時(shí),負(fù)責(zé)引導(dǎo)和緩存模塊。此外,將模塊標(biāo)識(shí)轉(zhuǎn)換為數(shù)字模塊 ID。這樣可以減少打包的大小和引導(dǎo)時(shí)間。

這是什么意思呢?我們來看看編譯輸出:

(function(modules) {
    // This is the module runtime.
    // It"s only included once per compilation.
    // Other chunks share the same runtime.
    var installedModules = {};
    // The require function
    function __webpack_require__(moduleId) {
        ...
    }
    ...
    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 1);
})
([ // An array that maps module ids to functions
    // a.js as module id 0
    function (module, __webpack_exports__, __webpack_require__) {
        "use strict";
        Object.defineProperty(__webpack_exports__, "a", {
            configurable: false,
            enumerable: true,
            get: () => number
        });

        let number = 42;

        function incr() {
            number++;
        }
    },
    // test.js as module id 1
    function (module, __webpack_exports__, __webpack_require__) {
        "use strict";
        var __WEBPACK_IMPORTED_MODULE_0__a__ = __webpack_require__(0);

        // Object reference as "live binding"
        console.log(__WEBPACK_IMPORTED_MODULE_0__a__["a" /* number */]);
    }
]);

簡化的 webpack 輸出,模擬 ES Modules 行為

結(jié)果已經(jīng)簡化并刪除了一些與此示例無關(guān)的代碼。你會(huì)發(fā)現(xiàn),webpack 在 exports 對象上將所有 export 語句替換成 Object.defineProperty,并使用屬性訪問器替換對引入值的所有引用。還要注意每個(gè) ESM 開始時(shí)的 "use strict" 指令,這是由 webpack 自動(dòng)添加,在 ESM 中必須是嚴(yán)格模式。

這種實(shí)現(xiàn)只是模擬,因?yàn)樗噲D模仿 ESM 和 CJS 的行為 -- 但不是與其完全保持一致。比如,這種模擬并不符合某些邊緣情況??聪旅孢@個(gè)模塊:

console.log(this);

如果你通過加上 babel-preset-es2015 的 Babel 來運(yùn)行,結(jié)果是:

"use strict";
console.log(undefined);

從輸出結(jié)果可以看出,Babel 假設(shè)默認(rèn)是 ESM,因?yàn)?module 模式即代表嚴(yán)格模式,在嚴(yán)格模式下會(huì)將 this 初始化為 undefined。

然而,使用 webpack,結(jié)果是:

(function(module, exports) {

console.log(this);

})

在引導(dǎo)模塊時(shí),this 將指向 exports ,與 Node.js 使用的 CJS 行為一致。這是因?yàn)檎Z法上不確定是 script 還是 module,解析器無法判斷該模塊是 ESM 還是 CJS。在不明確的時(shí)候,webpack 會(huì)模擬 CJS,因?yàn)樗匀皇亲钍軞g迎的模塊風(fēng)格。

這種模擬其實(shí)已經(jīng)包含了很多情況,因?yàn)槟K作者通常會(huì)避免這種代碼。然而,“很多情況”對于像 Node.js 這樣的平臺(tái)是不夠的,因?yàn)樗枰WC所有有效的 JavaScript 代碼都能正常運(yùn)行。

Node.js 中的 ESM

Node.js 在執(zhí)行 ESM 時(shí)遇到了麻煩,因?yàn)槿匀恍枰С?CJS,語法看起來相似,但運(yùn)行時(shí)行為完全不同。Node.js 核心技術(shù)委員會(huì)(CTC)成員 James M Snell 撰寫了一篇很好的文章來解釋 CJS 與 ESM 之間的差異。

歸結(jié)起來,CJS 是一個(gè)動(dòng)態(tài)模塊系統(tǒng),ESM 是靜態(tài)模塊系統(tǒng)。

CJS

允許動(dòng)態(tài)同步 require()

導(dǎo)出僅在模塊執(zhí)行后才知道

導(dǎo)出可以在模塊初始化后添加,替換和刪除

ESM

只允許靜態(tài)同步 import

在模塊執(zhí)行之前,導(dǎo)入和導(dǎo)出已經(jīng)關(guān)聯(lián)

導(dǎo)入和導(dǎo)出是不可變的

由于 CJS 早于 ES2015,所以一直在 script 模式下解析,封裝通過使用函數(shù)包裝器實(shí)現(xiàn)。在 Node.js 中加載 CJS,實(shí)際上會(huì)執(zhí)行與此類似的代碼:

const module = {
    exports: {}
};
const require = makeRequireFunction();
const filename = "...";
const dirname = "...";
(function (exports, require, module, __filename, __dirname) {
/* YOUR CODE */
})(module.exports, require, module, filename, dirname);

對 Node.js 的 CommonJS 模塊的簡單函數(shù)包裝

問題出現(xiàn)了,將兩個(gè)模塊系統(tǒng)集成到同一個(gè)運(yùn)行時(shí)時(shí),ESM 和 CJS 之間的循環(huán)依賴可能會(huì)迅速導(dǎo)致類似死鎖的情況。

而且,由于現(xiàn)有 CJS 模塊數(shù)量龐大,也不能直接放棄對 CJS 的支持。為了避免 Node.js 生態(tài)的中斷,有兩點(diǎn)已經(jīng)很明顯:

現(xiàn)有的 CJS 代碼必須以相同的方式繼續(xù)工作

兩個(gè)模塊系統(tǒng)都必須同時(shí)且盡可能無縫地工作

目前的權(quán)衡

2017 年 3 月,經(jīng)過幾個(gè)月的討論,CTC 終于找到了一種解決問題的辦法。由于在 ES 規(guī)范和引擎不改變的情況下無法進(jìn)行無縫集成,CTC 決定開始一些權(quán)衡之后的實(shí)現(xiàn)工作:

1.ESM 必須是 *.mjs 文件擴(kuò)展名

這是由于上面提及的模糊語法問題,無法通過解析來確切知曉 JavaScript 代碼是什么類型。為了 Node.js 向后兼容的目標(biāo),作者需要加入一種新模式。已經(jīng)有關(guān)于各種替代品的討論,但使用不同文件擴(kuò)展名是解決目前問題的最佳權(quán)衡。

2.CJS 只能異步導(dǎo)入 ESM import()

Node.js 將異步加載 ESM,以便盡可能接近瀏覽器的行為。因此,同步的 require() 在 ESM 是不可能的,并且依賴于 ESM 的每個(gè)功能都需要異步:

const driverPromise = import("dbdriver");

exports.readFromDb = async (query) => {
   return (await driverPromise).read(query);
};
3. CJS 向 ESM 暴露一個(gè)不可變的默認(rèn)導(dǎo)出

使用 Babel 或 Webpack,我們通常將 CJS 重構(gòu)為 ESM,如下所示:

// CJS
const { a, b } = require("c");
// ESM
import { a, b } from "c";

再一次地,他們的語法看起來很相似,但忽略了 CJS 中沒有命名導(dǎo)出的事實(shí)。只有一個(gè)叫做 default 的導(dǎo)出,等同于在 CJS 模塊完成計(jì)算后一個(gè)不可變的 module.exports 。從技術(shù)上講,有可能將 module.exports 解構(gòu)成命名導(dǎo)入,但這需要對標(biāo)準(zhǔn)作更大的變更。這就是現(xiàn)在 CTC 決定采取這種方式的原因。

4.模塊范圍的變量類似 module,require 以及 __filename 在 ESM 不存在

Node.js 和瀏覽器會(huì)實(shí)現(xiàn)一些 ESM 的特性,但標(biāo)準(zhǔn)化過程仍在進(jìn)行中。

鑒于將 CJS 和 ESM 集成到一個(gè)運(yùn)行時(shí)的工程挑戰(zhàn),CTC 在評估邊緣情況和權(quán)衡方面做了非常好的工作。比如使用不同的文件擴(kuò)展名是就是一個(gè)很簡單的解決方案。

實(shí)際上,一個(gè)文件擴(kuò)展名可以認(rèn)為是一個(gè)二進(jìn)制文件如何解釋的提示。如果一個(gè) module 不是 script,我們應(yīng)該使用不同的文件擴(kuò)展名。其他工具(如 linter 或 IDE )也可以獲取相同信息。

當(dāng)然,引入新的文件擴(kuò)展名有成本,但是一旦服務(wù)器和其他應(yīng)用程序確認(rèn) *.mjs 為JavaScript,我們很快就會(huì)忘記這個(gè)爭議。

將 * .mjs 作為 Node.js 的 Python 3?

考慮到所有這些限制,人們可能會(huì)問,這種過渡將對現(xiàn)在的生態(tài)造成什么樣的損害。雖然 CTC 會(huì)努力解決問題,但社區(qū)如何采用這一點(diǎn)仍然存在很大不確定性。這種不確定性 被眾多知名的 NPM 模塊作者 再次強(qiáng)調(diào),他們聲稱將不會(huì)在模塊中使用 *.mjs。

Python 3 is killing Python

很難預(yù)測社區(qū)如何反應(yīng),但是應(yīng)該不會(huì)對現(xiàn)在的生態(tài)造成大破壞,甚至能看到從 CJS 平穩(wěn)過渡到 ESM。主要有兩個(gè)原因:

1.與 CJS 嚴(yán)格向后兼容

那些不喜歡 ESM 的模塊作者可以繼續(xù)使用 CJS,保證自己不被排擠出局。這樣他們自己的代碼不會(huì)受到采用 ESM 的影響,降低遷移到另一個(gè)運(yùn)行時(shí)的可能性,讓 NPM 遷移到新生態(tài)變得容易。從 CJS 到 ESM 的重構(gòu)給包維護(hù)者帶來額外工作,不能指望所有人都有時(shí)間。

2. CJS 在 ESM 中的無縫整合

從 ESM 導(dǎo)入 CJS 模塊非常簡單。需要注意的是,CJS 僅導(dǎo)出一個(gè)默認(rèn)值。一旦處于 ESM,甚至可能根本不會(huì)注意到依賴關(guān)系使用的模塊風(fēng)格,尤其是與在 CJS 中使用 await import()相比。

由于 ESM 的這個(gè)優(yōu)點(diǎn)以及其他有點(diǎn),比如開箱即用的 tree shaking 和瀏覽器兼容性,預(yù)計(jì)在未來幾年內(nèi),我們可以看到向 ESM 的緩慢而穩(wěn)定的過渡。CJS 的特性,如動(dòng)態(tài) require() 和猴子補(bǔ)丁導(dǎo)出,在 Node.js 社區(qū)一直是有爭議的,不比 ESM 帶來的好處。

這些對我來說意味著什么?

因?yàn)樽罱@些事情,很容易對目前存在的所有選擇和限制感到困惑。在接下來,整理了開發(fā)人員面臨的典型問題以及我們的回答:

現(xiàn)在需要重構(gòu)現(xiàn)有的代碼嗎?

不需要。Node.js 才剛剛開始實(shí)現(xiàn) ESM,仍然有大量的工作要做。James M Snell 預(yù)計(jì)至少還需要一年時(shí)間,還有很多變化的余地,所以現(xiàn)在重構(gòu)是不安全的。

應(yīng)該在新代碼中使用 ESM 嗎?

如果你已經(jīng)有或者打算使用像 webpack 這樣的構(gòu)建工具,答案是肯定的。這將更容易完成代碼庫的過渡,并使 tree shaking 成為可能。但要小心:一旦 Node.js 支持原生 ESM,可能需要重構(gòu)其中的一些部分。

如果你正在編寫一個(gè)庫,答案是也肯定的,你的模塊使用者將受益于 tree shaking。

如果你不想進(jìn)行構(gòu)建操作,或者正在編寫一個(gè) Node.js 應(yīng)用程序,還是用 CJS 吧。

現(xiàn)在應(yīng)該使用 .mjs 嗎?

不要這樣做,目前沒有什么好處,工具支持依然薄弱。建議一旦原生 ESM 支持登陸 Node.js,盡快開始遷移。記住,瀏覽器只關(guān)心 MIME 類型,而不是文件擴(kuò)展名。

應(yīng)該關(guān)心瀏覽器兼容性嗎?

是的,需要在一定程度上關(guān)注這個(gè)問題。 不應(yīng)該在導(dǎo)入語句中省略 .js 擴(kuò)展名,因?yàn)闉g覽器需要完整的 URL,無法像 Node.js 這樣執(zhí)行路徑查詢。同樣,應(yīng)該避免 index.js 文件。不過,人們并不會(huì)很快在瀏覽器中使用 NPM 軟件包,因?yàn)槿匀徊荒?bare 導(dǎo)入。

作為庫作者該怎么辦?

用 ESM 編寫代碼,并使用 Rollup 或 Webpack 轉(zhuǎn)換成單個(gè) CJS 模塊,然后在 package.jsonmain 字段指向此 CJS 包,并將 module 字段指向原始 ESM。如果還使用 ESM 之外的其他新語言功能,則應(yīng)編譯成 ES5,并提供 CJS 和 ESM 的打包。這樣,你的庫用戶仍然可以從 tree shaking 獲利而無需對代碼進(jìn)行轉(zhuǎn)換。

看一下這些完成 tree shaking 的模塊

總結(jié)

關(guān)于 ES 模塊有很多不確定性。由于目前 Node.js 在實(shí)現(xiàn)上的權(quán)衡,開發(fā)人員擔(dān)心可能會(huì)破壞 Node.js 的生態(tài)。

這些還不會(huì)發(fā)生,有兩個(gè)原因:CJS 的嚴(yán)格的后向兼容和 CJS 在 ESM 中的無縫集成。

在 Node.js 發(fā)布原生 ESM 支持之前,應(yīng)該仍然使用 Rollup 和 Webpack 等工具。它們在一定程度上模擬了 ESM 環(huán)境,但要注意它們不完全符合規(guī)范。此外,使用打包仍然是個(gè)很好的選擇,一旦可以在瀏覽器中使用 NPM 軟件包。

我們 webpack 團(tuán)隊(duì)正在努力做一些工作,幫助開發(fā)者平穩(wěn)過渡。為了這個(gè)目標(biāo),我們計(jì)劃在 Node.js 的 ESM 支持成熟后,模擬 Node.js 導(dǎo)入 CJS 的方式。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/83216.html

相關(guān)文章

  • 圖說 WebAssembly(六):現(xiàn)狀與展望

    摘要:現(xiàn)狀年月日,主流的四大瀏覽器達(dá)成了共識(shí)并宣布的最小可行產(chǎn)品已經(jīng)完成。更快的函數(shù)調(diào)用當(dāng)前,在中調(diào)用函數(shù)比想象的要慢。直接操作目前,沒有任何方式能夠操作。這就導(dǎo)致了部分應(yīng)用可能會(huì)因此而推遲發(fā)布時(shí)間。結(jié)束現(xiàn)如今已經(jīng)相當(dāng)快速。 本文是圖說 WebAssembly 系列文章的最后一篇。如果您還未閱讀之前的文章,建議您從第一篇入手。 現(xiàn)狀 2017 年 2 月 28 日,主流的四大瀏覽器達(dá)成了共識(shí)...

    clasnake 評論0 收藏0
  • 王下邀月熊_Chevalier的前端每周清單系列文章索引

    摘要:感謝王下邀月熊分享的前端每周清單,為方便大家閱讀,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清單系列,并以年月為單位進(jìn)行分類,具體內(nèi)容看這里前端每周清單年度總結(jié)與盤點(diǎn)。 感謝 王下邀月熊_Chevalier 分享的前端每周清單,為方便大家閱讀,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清單系列,并以年/月為單位進(jìn)行分類,具...

    2501207950 評論0 收藏0
  • 7000位程序員揭秘2019 軟件開發(fā)現(xiàn)狀:看Python、Java與Go,誰主沉浮

    摘要:在本次受訪者中,也有的開發(fā)者表示主要使用框架。這不剛發(fā)布了三個(gè)月,就已進(jìn)入了特性凍結(jié)階段。根據(jù)官方統(tǒng)計(jì),有的開發(fā)人員使用進(jìn)行單元測試,而的人使用。此外,與開發(fā)者有所不同,開發(fā)者更習(xí)慣使用。對于語言的使用,表示,多數(shù)人使用單個(gè)全局。 showImg(https://upload-images.jianshu.io/upload_images/13825820-feaee185c3c95b...

    cgh1999520 評論0 收藏0
  • 前端每周清單年度總結(jié)與盤點(diǎn)

    摘要:前端每周清單年度總結(jié)與盤點(diǎn)在過去的八個(gè)月中,我?guī)缀踔蛔隽藘杉?,工作與整理前端每周清單。本文末尾我會(huì)附上清單線索來源與目前共期清單的地址,感謝每一位閱讀鼓勵(lì)過的朋友,希望你們能夠繼續(xù)支持未來的每周清單。 showImg(https://segmentfault.com/img/remote/1460000010890043); 前端每周清單年度總結(jié)與盤點(diǎn) 在過去的八個(gè)月中,我?guī)缀踔蛔隽?..

    jackwang 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<