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