国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

為何 ES Module 如此姍姍來遲

xuexiangjys / 1914人閱讀

摘要:最大的好處是對用戶而言透明,可惜原因如前所述,此方案已否定。鑒于已經在正式提案中,倘若討論持續僵持不下,不出意外將會隨著時間推移而正式成為規范。月碰頭會的與會者紛紛表示這次會議進展令人愉快,會議內容匯總在此,以及一些補充。

說明:本文發布之后,此問題的推進峰回路轉,不停有新內容。文末新增一節 Updates,跟進本文發布之后的 ES Module 標準化進展情況。

瀏覽器大戰多年了熱度依舊高漲,大家終于在 JS 新特性的部署上達成一致紛紛追趕最新標準,然而 ES2015 中的 ES Module 這個萬眾期待的重要特性卻始終遲遲未能實現。

等 2020 年回望歷史,倘若我們錯過了 ES Module 這艘船而 Node.js 死在汪洋大海之中,沒有任何其他技術問題的重要性可以與此相比。
-- issac

Module 的規范是完工了的,只是對于模塊如何加載和解析留給了“實現環境決定”——按歷史經驗,問題往往就出現在這一環。當然了不是燙手山芋 W3C 也不會就這么輕松甩開對吧,事實上這也不是 W3C 一家的事情,牽涉到 TC39、Node 技術委員會、Node 和前端兩個開發社群,以及 npm 公司。

故事很長,我們從頭說起。importexport 的語法規范很明確,模塊的解析器 V8 早已實現,萬事俱備只欠加載。區區加載能有多麻煩?

Module 的特性

在新規范下,JavaScript 程序劃分成兩種類型:腳本(我們以前寫的傳統JS)和模塊(ES規范中新定義的 Module),模塊有四項于腳本不同的特性:

強制嚴格模式(無法取消)

執行環境在一個非全局的作用域中

可以使用 import 導入其他 Module 的 binding

可以使用 export 導出本 Module 的 binding

看上去規則簡單明白,但是要讓一個解析器(parser)區分兼容這兩種模式還挺復雜的。

解析器的難題
看看代碼中是否包含 importexport 關鍵字不就可以判斷它的類型了么?

不行。首先猜測用戶意圖是個危險行為,如果你猜對了,就更加掩蓋了猜錯可能會造成的風險。

而嚴格模式,除了運行時的一些要求之外還定義了幾個語法錯誤:

使用 with 關鍵字;

使用八進制字面量(如 010);

函數參數重名;

對象屬性重名(僅在 ES5 環境。ES6 取消了此錯誤);

使用 implementsinterfaceletpackageprivateprotectedpublicstaticyield 作為標識符。

這些語法錯誤需要在解析時就拋出來。所以如果以腳本模式解析到了文件末尾才發現有 export,就得從頭重新解析整個文件來捕捉上述語法錯誤。

那我們換一條路,開始先假定為模塊進行解析代碼。既然 Module 語法相當于嚴格模式 + 導入導出 (importexport),我們可以用腳本模式 + 導入導出的語法來解析整個文件。然而這種解析規則已經超越了規范定義,這么扭曲的路線可以預見它成為 Bug 源泉的樣子。

危險但不是不可能。OK 真正的麻煩來了:按照規范 importexport 都是可選的——你可以寫一個 Module,既不導入也不導出任何東西,它只是對全局作用域做些小動作,比如這樣:

// 一個合法的 Module
window.addEventListener("load", function() {
    console.log("Window is loaded");
});
// WAT!

總的來說,包含 importexport 表明它一定是個 Module,但沒有這兩個關鍵字卻不能證明它不是 Module。 ╮(╯_╰)╭

區分 JavaScript 文件類型的任務沒法放在解析器里自動完成,我們需要在解析文件之前就知道它的類型。

瀏覽器的辦法

這就是為什么瀏覽器的模塊引用是這個寫法:

當瀏覽器開始加載這個 foo.js,它會邊加載邊解析,碰到 import { bar } from "./bar.js" 的第一時間開始加載依賴的 bar.js,加載完之后對其解析,檢查其中是否導出了 bar。如此往復完成整個 Module 的解析。

Node.js 呢

到了 Node.js,新的問題來了。

作為世界上最大的軟件包倉庫,npm 中現有的軟件包都是 CommonJS 規范。ES Module 需要能夠與 CommonJS 模塊共存,允許開發者們逐步轉向新的語法。

所謂的共存,主要是指 import { foobar } from "foobar" 語法要支持 CJS Module 和 ES Module 兩種包格式——如果 import 只能用來導入 ES Module 而 require 可以導入任意模塊,那么所有人都會用 require;如果 importrequire 各自負責導入各自的格式,那么開發者就需要知道所有依賴的庫的格式,使用相應語法來導入它,并且在依賴的庫們更換到新格式的時候修改自己的代碼去兼容……在可預見的 CommonJS -> ES Module 漫長過渡期里這樣的負擔對社區而言不可接受。

為此社區提出了不少方案,(好消息)經過大量的討論之后現在已經集中到兩個選擇還在討論:

解析器自動檢測。最大的好處是對用戶而言透明,可惜原因如前所述,此方案已否定。

使用 "use module" 標注。一想到 JS 的未來永遠都要在文件開頭貼這么個膏藥大家就不能忍了。否定。

新的文件后綴 .jsm。主要問題是現有社區工具鏈全部需要更新才能支持,另外和瀏覽器實現的統一也要考慮。

package.json 上發揮。這個門類下的提議就更多了,比如添加一個 module 字段逐步替代掉 main

{
    // ...
    "module": "lib/index.js",
    "main": "old/index.js",
    // ...
}

這個方案只適用單入口的情況,對多文件(比如 require("foo/bar.js")的場景)就不行了。那就改成 modules 字段(復雜度陡升):

{
    // ...
    // files:
    "modules": ["lib/hello.js", "bin/hello.js"],

    // directories:
    "modules": ["lib", "bin"],

    // files and directories:
    "modules": ["lib", "bin", "special.js"],

    // if package never uses CJS Modules
    "modules": ["."],
}

這還沒完,更多方案就不詳述了,大家可以到 Node.js Wiki 上查看。

就個人偏好而言,盡管所有的方案都有利有弊,而 package.json 這條路為了兼容各種需求,修改版的提案已經越來越復雜,比較起來 .jsm 后綴倒是愈發顯得簡單清晰了。我更喜歡這個干凈的解決方案。

現在的進展(2016.04.15)

閱讀需要支付1元查看
<