摘要:最大的好處是對用戶而言透明,可惜原因如前所述,此方案已否定。鑒于已經在正式提案中,倘若討論持續僵持不下,不出意外將會隨著時間推移而正式成為規范。月碰頭會的與會者紛紛表示這次會議進展令人愉快,會議內容匯總在此,以及一些補充。
說明:本文發布之后,此問題的推進峰回路轉,不停有新內容。文末新增一節 Updates,跟進本文發布之后的 ES Module 標準化進展情況。
瀏覽器大戰多年了熱度依舊高漲,大家終于在 JS 新特性的部署上達成一致紛紛追趕最新標準,然而 ES2015 中的 ES Module 這個萬眾期待的重要特性卻始終遲遲未能實現。
等 2020 年回望歷史,倘若我們錯過了 ES Module 這艘船而 Node.js 死在汪洋大海之中,沒有任何其他技術問題的重要性可以與此相比。
-- issac
Module 的規范是完工了的,只是對于模塊如何加載和解析留給了“實現環境決定”——按歷史經驗,問題往往就出現在這一環。當然了不是燙手山芋 W3C 也不會就這么輕松甩開對吧,事實上這也不是 W3C 一家的事情,牽涉到 TC39、Node 技術委員會、Node 和前端兩個開發社群,以及 npm 公司。
故事很長,我們從頭說起。import 和 export 的語法規范很明確,模塊的解析器 V8 早已實現,萬事俱備只欠加載。區區加載能有多麻煩?
Module 的特性在新規范下,JavaScript 程序劃分成兩種類型:腳本(我們以前寫的傳統JS)和模塊(ES規范中新定義的 Module),模塊有四項于腳本不同的特性:
強制嚴格模式(無法取消)
執行環境在一個非全局的作用域中
可以使用 import 導入其他 Module 的 binding
可以使用 export 導出本 Module 的 binding
看上去規則簡單明白,但是要讓一個解析器(parser)區分兼容這兩種模式還挺復雜的。
解析器的難題看看代碼中是否包含 import 和 export 關鍵字不就可以判斷它的類型了么?
不行。首先猜測用戶意圖是個危險行為,如果你猜對了,就更加掩蓋了猜錯可能會造成的風險。
而嚴格模式,除了運行時的一些要求之外還定義了幾個語法錯誤:
使用 with 關鍵字;
使用八進制字面量(如 010);
函數參數重名;
對象屬性重名(僅在 ES5 環境。ES6 取消了此錯誤);
使用 implements、interface、let、package、private、protected、public、static 或 yield 作為標識符。
這些語法錯誤需要在解析時就拋出來。所以如果以腳本模式解析到了文件末尾才發現有 export,就得從頭重新解析整個文件來捕捉上述語法錯誤。
那我們換一條路,開始先假定為模塊進行解析代碼。既然 Module 語法相當于嚴格模式 + 導入導出 (import 和 export),我們可以用腳本模式 + 導入導出的語法來解析整個文件。然而這種解析規則已經超越了規范定義,這么扭曲的路線可以預見它成為 Bug 源泉的樣子。
危險但不是不可能。OK 真正的麻煩來了:按照規范 import 和 export 都是可選的——你可以寫一個 Module,既不導入也不導出任何東西,它只是對全局作用域做些小動作,比如這樣:
// 一個合法的 Module window.addEventListener("load", function() { console.log("Window is loaded"); }); // WAT!
總的來說,包含 import 或 export 表明它一定是個 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;如果 import 和 require 各自負責導入各自的格式,那么開發者就需要知道所有依賴的庫的格式,使用相應語法來導入它,并且在依賴的庫們更換到新格式的時候修改自己的代碼去兼容……在可預見的 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)已經加入 HTML 規范,WhatWG 剛剛發了一篇文章講述他們如何經過艱苦卓絕的努力達成這一目標,接下來就看瀏覽器廠商實現了。
除此之外 WhatWG 手上還有一個 ES Module loader 規范,用于指定 Module 的動態加載方式。它曾經是 ES6 草案的一部分,但因為 ES2015 “要趕著發布來不及了”不幸被砍,目前歸屬 WhatWG 推進。
Node.js 這邊,在相當一段時間里我們還要借助 transpiler 來體驗 ES Module。這件事需要 V8、Node.js、WhatWG 共同協調完成。
按計劃本月 Node.js 發布 6.0,順利的話可以 確定集成 V8 5.0(BTW,一天后 V8 發布了 5.1),對 ES2015 的特性支持達到 93%——看來 ES Module 很可能會成為 “The last ES2015 feature” 了。
關注 ES Module 的進展,還可以看看幾個地方:
Node 社區提案和討論:https://github.com/nodejs/nod...
V8 的實現:https://bugs.chromium.org/p/v...
Blink 的實現:https://bugs.chromium.org/p/c...
愿 ES Module 早日到來。
Updates關于 ES Module 在 Node.js 環境下的識別方案,從一月份 bmeck 提出提案開始社區就持續溝通和爭論,以下是相關進展更新。
2016.01.08
bmeck 提出關于 ES Module 的提案(增加新后綴.mjs),社區討論開始。
2016.02.06
社區提的方案歸納起來,有四個方向。
2016.04.15
本文發布的日子。
2016.04.20
經過兩個月的密集討論,四個方向只剩下兩個存活:.mjs 派和 package.json 派,然而這兩派的爭論非常激烈。
2016.04.27
鑒于 .mjs 已經在正式提案中,倘若討論持續僵持不下,不出意外 .mjs 將會隨著時間推移而正式成為規范。懷著這樣的危機感,package.json 派發起了 In defense of dot js 來抗衡 .mjs 的提案,要求保持 .js 后綴不變而使用 package.json 來識別 ES Module。
2016.06.14
重大轉折!bmeck 提出一個新的方案 UnambiguousJavaScriptGrammar:既然兩邊的糾結都是因為無法從文件本身識別 ES Module 而起,不妨調整一點語法細節(ES Module 中的 exports 語句不再是可選的,至少有一句 exports {} 來表明該文件是個 ES Module),兩派的爭論就這么迎刃而解了!
2016.07.06
經過 Node.js TSC 的討論,Unambiguous JavaScript Grammar 方案正式加入提議(proposal)。
2016.07.07
雖然 Unambiguous JavaScript Grammar 加入了 Node.js 的草案提案(5.1章),但是考慮到距離 TC39 的七月會議只剩下一周時間,而 Node.js 這邊希望做更充分的調研和測試再進行討論,所以從這次 TC39 的議程中拿掉了。
2016.09.06
Domenic 提了 import() 作為動態加載的方案,有望取代 System.import() 或 System.loader.import()。
2016.09.17
ES Module 再次提上 TC39 的議事日程,相關的還有內建模塊和 import()。
2016.09.30
TC39 9月碰頭會的與會者紛紛表示這次會議進展令人愉快,會議內容匯總在此,以及一些補充。
Node.js 開發者想要提出一些修改規范的建議,也不知道合適不合適,溝通之后發現 TC39 是非常關心和在意每個社區的需求的(大家相談甚歡)。
原本的 ES 規范要求模塊加載過程需要先完成靜態 parse 然后再 evaluate,但是現在的 Node.js CommonJS 模塊無法滿足這個要求(CJS 模塊必須 evaluate 之后才知道 exports 的是什么)。討論下來規范將會改為允許 parse 過程在碰到 import CJS 模塊時進入一個掛起的狀態,等待依賴樹中的 CJS 模塊 evaluate 之后再完成 parse。
對模塊類型的檢測目前是三個方案選項:
Unambiguous JavaScript Grammar 看上去比較簡單,但實現起來還是有不少坑;
package.json 的辦法比較累贅,局限也多;
.mjs 的方案最簡單,看來是最可行的,而且也跟 Node.js 現有方式一致(用后綴 .node、.json、.js來區分加載類型)。除非 Unambiguous JavaScript Grammar 的實現問題都解決掉,否則最終方案就是它了。
import() 大家都覺得沒問題,穩步推進中。
由于 ES Module 的靜態特性,以前給 CJS 模塊做動態 Mock、MonkeyPatch 的方式都不行了。不過解決辦法也有,一是在加載階段提供鉤子,二是允許對已經加載的模塊做熱替換。
2017.02.12
Node.js CTC 和 TC39 的討論:
由于 ES6 模塊的異步特性,require() 將無法加載 ES6 模塊。
Babel 目前支持的 import { foo } from "node-cjs-module" 也不符合規范,想 import 一個 NCJS 模塊的話只能 import m from "node-cjs-module" 然后 m.foo() 調用。
.mjs 是問題最少的選擇。
(悲傷的消息來了)就目前剩余的工作內容估計,距離 ES6 Module 最終實現大約還有至少一年的時間(往好的一面想,終于看得到 timeline 了)。
2017.05.10
bmeck 在 Twitter 表示已經實現了 .mjs 加載器的原型,在 Node.js v9 中可以用 flag 的方式啟用,(希望)在 v10 中正式推出。也就是還有一年的時間,一切順利的話 2018 年 4 月就能看到 ES Module 正式加入 Node.js LTS。
2017.05.11
工具鏈對 .mjs 后綴的支持都在推進中:
Babel: https://github.com/babel/babe...
Babili/babel-minify already supports .mjs: https://github.com/babel/babi...
AVA: https://github.com/avajs/ava/...
Visual Studio Code: https://github.com/Microsoft/...
2018.03.30
Node.js 項目中和 ES Module 實現相關的 Issue 和 PR
2018.04.25
Node.js 10.0.0 發布,加入了對 ES Module 的實驗性支持(需要 --experimental-modules 開啟)
https://github.com/nodejs/nod...
2019.03.28
新版 ES Module 設計定案,PR 合進主干(https://github.com/nodejs/nod...),特性有變,仍然使用 --experimental-modules 開啟。目前的計劃是趕上 4 月份 Node.js 12 發布,最終在 2019 年 10 月進入 LTS。
https://www.nczonline.net/blo...
https://github.com/nodejs/nod...
http://awal.js.org/blog/es6/2...
http://es2015-node.js.org/
https://medium.com/@jasnell/a...
http://2ality.com/2017/05/es-...
https://github.com/nodejs/mod...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79200.html
摘要:為何有查閱了的文檔,并沒有找到字段的定義,直到才知道它是中最早就提出的概念。況且目前大部分仍是采用,所以便使用了另一個字段。所以目前主流的打包工具都是支持的,鑒于其優點,字段很有可能加入的規范之中。 引入 最近團隊的一個同學在搞 npm library 源碼的調試插件,因為內部的一個組件庫含有大量的邏輯,在某個項目中不經意就出現一個磨人的 bug,但是組件庫發布都是打包編譯后的代碼,而...
摘要:于是模塊化的需求也就產生了。現存的規范還真不少,不知道能否終結這場混戰和分別是和定義的標準。是使用的模塊化標準。的模塊化聲明的方式與類似,只是更進一步,天然隔離了命名空間。其他,關于前端化趨勢模塊化意味著什么更強大的前端,技術整體前移。 作者:肖光宇野狗科技聯合創始人,先后在貓撲、百度、搜狗任職,愛折騰的前端工程師。野狗官博:https://blog.wilddog.com/ 野狗官網...
摘要:目錄引言環境需求分析前置準備逛店鋪流程回顧代碼全景展示總結引言最近疊貓貓的活動可真是十分的火爆,每天小伙伴們為了合貓貓忙的可謂是如火如荼。為啥要疊貓貓呢賺貓幣得現金紅包眼看為實,先來看看我朋友的購物訂單。 目錄:0 引言1 環境2 需求分析3 前置準備4 逛店鋪流程回顧5 代碼全景展示6 總結 0 引言 最近疊貓貓的活動可真是十分的火爆,每天小伙伴們為了合貓貓忙的可謂是如火如荼。為啥要...
摘要:既可以通過一個配置文件使用命令行接口來調用,也可以他自己的使用。使用最簡單的方法就是通過命令行接口。命令縮寫會以監視模式運行。這時運行下將不會有錯誤拋出,包含導入的組件。 介紹 概覽 rollup是一個js打包器,用來將很細碎的js編譯打包成大的復雜的東西,像是一個庫或者一個應用。其使用了ES6自帶的新標準來格式化和打包js代碼,而不是原先的Commonjs或者AMD這類解決方案。ES...
閱讀 1076·2021-11-22 14:56
閱讀 1520·2019-08-30 15:55
閱讀 3359·2019-08-30 15:45
閱讀 1655·2019-08-30 13:03
閱讀 2868·2019-08-29 18:47
閱讀 3334·2019-08-29 11:09
閱讀 2641·2019-08-26 18:36
閱讀 2615·2019-08-26 13:55