- 原文地址:We’re nearing the 7.0 Babel release. Here’s all the cool stuff we’ve been doing.
- 原文作者:Henry Zhu
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:xueshuai
- 校對者:Colafornia
編輯:我已經離開了 Behance 並製作了 Patreon,嘗試 全職投入到開源工作上,請你或你的公司考慮捐獻。
Babel 簡介
有些人可能把 Babel 看作是一個讓你寫 ES6 程式碼的工具。更準確的說,Babel 是把 ES6 程式碼轉換為 ES5 程式碼的 JavaScript 編譯器。當它的名字是 6to5 時,這很適合,但是我認為 Babel 已經變得不只是這些了。
現在讓我們聊一些背景。這一點非常必要,因為不像執行在伺服器上的其他語言(甚至是 Node.js),你能執行的的 JavaScript 版本取決於你的瀏覽器的特定版本。如果你使用最新的瀏覽器,而你的使用者(你想留住的)仍然使用 IE,是沒有關係的。但是,舉個例子,如果你想寫 class A {}
,那麼你就很不幸了 - 你的一些使用者將會得到一個 SyntaxError
和一個空白頁面。
所以這就是建立 Babel 的原因。它允許你寫你想寫的 JavaScript 版本,你知道它會在你支援的所有(更老)的瀏覽器上正確的執行。
但是,它並不停止於 “ES6”(有些人喜歡說 ES2015)。Babel 已經確實擴充套件了他的最初目標,即只編譯 ES6 程式碼。現在,它能夠編譯任意一個你想要的 ES20XX 版本(JavaScript 最新版本)到 ES5.
正在進行的程式
關於這個專案的一個有趣的事情是,只要 JavaScript 語法被加進去,Babel 就需要實現一個轉換器去轉化它。
但是你可能會想,為什麼我們要給已經支援該語法的瀏覽器傳送一個編譯過的版本(更大的程式碼體積)?我們怎麼知道每個瀏覽器支援什麼語法?
好了,我們製作了一個讓你能夠指定你支援哪個瀏覽器的工具 babel-preset-en,來幫助解決這個問題。它將會自動地只轉換那些瀏覽器原生不支援的東西。
除此之外,Babel(因為它在社群的使用)能夠影響 JavaScript 語言自身的未來。鑑於它是用於轉換 JS 程式碼的工具,它也可以用於實現提交給 TC39 的任何提案(Ecma Technical Committee 39,一個使 JavaScript 作為標準向前發展的組織)。
一個 “提案” 進入到語言中會經歷從 Stage 0 到 Stage 4 的一個完整流程。Babel,作為一個工具,在正確的地方測試新的想法並且讓開發者在他們的應用中使用它,以便他們可以向委員會提供反饋。
出於以下原因,這一點非常重要:委員會想確信他們做出的更改是社群想要的(一致,直觀,有效)。在瀏覽器裡實現一個不明的想法是緩慢的(瀏覽器中的 C++ 對比 Babel 中的 JavaScript),昂貴的,並且要求使用者在瀏覽器中使用一個標誌,而不是更改他們的 Babel 配置檔案。
因為 Babel 已經非常普及了,這是有一個好的機會使真正的用途出現。這將使提案比那個沒有廣大社群開發者反饋的實現更好。
而且他不只是在產品中有用。我們的線上 REPL 對人們學習 JavaScript 有幫助,並且允許他們測試東西。
我認為 Babel 是一個能夠讓程式設計師們瞭解 JavaScript 如何工作的非常好的工具。通過給這個專案做貢獻,他們將會學習很多其他的概念,例如 AST,編譯器,語言規範等等。
我們真的對這個專案的未來很興奮,並且迫不及待的要知道這個團隊能夠走到哪裡。請加入並幫助我們!
我的故事
這裡有一些我希望每天都工作在這個專案上的原因,特別是作為一個維護者。大多數當前的維護者,包括我自己,並不是建立這個專案的人,而是在一年之後加入的 - 想起我開始的地方仍然很興奮。
至於我,我認識到一個需求和一個有趣的專案。我慢慢並持續地有更多的參與,現在我已經能夠有了我的僱主,Behance,資助我一半的時間工作在 Babel 上。
有時候“維護”只是意味著修復 bugs,在 Slack 或 Twitter 上回答問題,或是編寫更新日誌(這真的取決於我們每一個人)。但是最近,我已經減少了在 bug 修復和特性上的注意力。取而代之的是我把時間放在了思考更多高層次的問題上,例如:這個專案的未來是什麼?我們如何就維護者與使用者的數量而言,使我們的社群增長。我們如何在資金方面維持這個專案?我們如何整體的融入 JavaScript 生態系統(教育,TC39,工具)?這裡有沒有一個適合我們的角色來幫助新人加入開源專案(RGSoC 和 GSoC)?
因為這些問題,這個版本最讓我感到興奮的地方不是那些特性(有很多:初步實施的新提案比如 Pipeline Operator (a |> b),有 TS 團隊提供幫助的 new TypeScript preset 和 .babelrc.js 檔案)。
而是我對這些特性所代表的東西更興奮:一年來不懈的努力不去打破一切,平衡使用者的期望(為什麼構建這麼慢/程式碼輸入這麼大,為什麼程式碼沒有足夠的相容,為什麼這個工作不能沒有配置,為什麼這裡沒有一個對其他情況的選擇)和維持一個由志願者組成的堅實團隊。
並且我知道我們的行業對“主要版本”有一個很大的關注,大肆宣傳特性和 stars,但那只是一個衰落的日子。我想建議我們繼續想一想什麼使生態系統以一個健康的方式持續向前推進。
這可能僅僅意味著思考維護者的心理和情緒上的負擔。這可能意味著思考如何提供指導,期望管理,平衡工作/生活的建議和其他的人們想要加入的資源,而不只是鼓勵開發者來期待即時的,免費的幫助。
深入更新日誌
嗯,我希望你能享受這個長的更新日誌 ?。如果你對幫助我們感興趣,請讓我們知道,我們將很樂意多談。
因為人們想更多地瞭解關於 Babel 如何工作和回饋,我們開放了一個新的 視訊頁面。這個頁面包含了 Babel 會議談話的視訊,來自團隊成員和社群人們的相關概念。
我們同樣建立了一個新的 團隊頁面!我們將在未來使用更多的關於人們在做什麼工作和為什麼他們加入的資訊來更新這個頁面。對於一個這樣大的專案,這裡有很多方法參與其中並幫忙。
這有一些亮點和簡短的事實:
- Babel 在 2017 年 10 月 28 日 已經 3 歲了!
- Daniel 把
babel/babylon
和babel/babel-preset-env
移動到 主要的 Babel monorepo 裡,babel/babel。這包含 Git 歷史,標籤和提案。 - 我們從 Facebook Open Source 收到了 每月 1000 美元的捐贈!
- 這是我們從一開始收到的最高的月度捐贈(下一個是 100 美元/月)。
- 同時,我們將用我們的資金去親自會見,並派人去 TC39 會議。這些會議每隔 2 個月在世界各地舉行。
- 如果一個公司想制定贊助什麼,我們能建立一個單獨的議題來跟蹤。這在之前是困難的,因為我們必須用從口袋掏錢支付,或者在同一周找到一個會議發言來幫助支付費用。
你怎麼才能幫忙
如果你的公司願意通過支援一個 JavaScript 開發的基礎部分和他的未來作為 回報,考慮一下給我們的 Open Collective 捐獻。你也能貢獻開發時間來幫助維護這個專案。
#1:幫助維護專案(工作中開發人員的時間)
對 Babel 最好的事情就是找願意幫助專案的人,他們能夠承擔大量的工作並對需求負責。再說一下,我從來沒覺得準備好 成為一個維護者,但不知道怎麼的,就發現已經這樣了。但是我只是一個人,並且我的團隊只有少數幾個人。
#2:幫助資助開發
我無疑是想能夠給團隊的人資金使他們能夠全職工作。尤其是 Logan,不久以前離開了他的工作並且用我們的資金來進行 Babel 的兼職工作。
#3 從其他渠道做貢獻
升級
我們也將升級幫助你 重寫你的 package.json/.babelrc 檔案 的工具和其他更多的東西。 理想情況下,這意味著它將修改任何必要的版本號變更,包的重新命名和配置的變更。
當嘗試更新的時候,請伸出手來幫助併發布議題。這是一個參與其中並幫助生態系統更新的絕好的機會。
前一個釋出 總結
- 放棄對 Node 0.10/0.12/5 的支援
- 更新 TC39 提案
- 數字分隔符:
1_000
- 可選的連結操作符:
a?.b
import.meta
(可解析)- 可選的 Catch 繫結:
try { a } catch {}
- BigInt(可解析):
2n
- 分割匯出擴充套件到
export-default-from
和export-ns-form
- 支援
.babelrc.js
(使用 JavaScript 代替 JSON 的配置) - 增加一個新的 Typescript Preset 和拆分 React/Flow presets
- 增加 JSX 分段支援 和 各種 Flow 更新
- 為了更小的體積,刪除內部
babel-runtime
依賴
最新更新的 TC39 提案
- Pipeline 操作符:
a |> b
- Throw 表示式:
() => throw 'hi'
- 無效合併操作符:
a ?? b
棄用年份 presets(例如,babel-preset-es20xx)
注意:使用 babel-preset-env
:
這比你決定使用哪個 Babel preset 更好的地方是什麼?已經為你完成了,自動地!
儘管維護資料列表的工作量很大 - 再一次,我們需要幫助 - 它解決了很多問題。它確保了使用者能夠及時瞭解規範。它意味著較少的配置/包的混亂。它意味著一個簡單升級之路。並且它意味著更少的什麼是什麼的問題。
babel-preset-env
其實是一個很 老 的 preset,它代替了每個其他的你將需要的(es2015,es2016,es2017,es20xx,latest 等等)句法 preset。
它代替了所有的老的 presets 來編譯最新年度的 JavaScript 版本(Stage 4 裡的無論什麼)。但是它也有能力根據你指定的目標環境編譯:它能夠處理開發模式,比如最新版本的瀏覽器,或是多重構建,例如 IE 版本。 它甚至有另一個為了流行多年的瀏覽器提供的版本
沒有移除 Stage presets(babel-preset-stage-x)
我們可以隨時更新它,或許我們只需要決定一個比當前 presets 更好的系統。
現在,stage presets 只是我們在每個 TC39 會議後手動更新的外掛列表。要使這個可管理,我們需要允許主要的版本為這些“不穩定”的包做緩衝。這是一部分,因為委員會無論怎樣都將重建這些包。所以我們可能會從一個官方包做這件事,並且之後我們有能力提供過更好的訊息等等。
重新命名:Scoped Packages(@babel/x
)
這裡是一個大約一年前我釋出的投票:
那時候,沒有很多專案使用 scoped packages,所以很多人甚至不知道它們的存在。那時候你可能必須為了一個 npm org 的賬戶花錢,而現在它是免費的(並且支援也 non-scoped packages)。
搜尋 scoped packages 的問題已經被解決了,下載計數也已經生效。唯一一個絆腳石就是一些第三方註冊仍然不支援 scoped packages。但是我想我們現在正處於一個點,在這裡看起來等待很不合理。
為什麼我們更喜歡 scoped packages:
- 命名困難:我們不需要去檢查別人是否決定在他們的外掛上使用我們的命名慣例
- 在 package squatting 上我們有類似的問題
- 有些時候人們建立
babel-preset-20xx
或是其他的包是因為好玩。我們必須釋出一個議題,並且發郵件把它要回來 - 有人有一個合法的包,但是它恰好和我們想叫的名字是一樣的
- 有人看到一個新的提案正在 merging(像 optional chaining 或者 pipeline 操作符),並且決定使用同樣的名字來 fork 和 publish。然後,當我們釋出的時候,我們就被告知包已經被髮布了 ?。所以我必須找到他們和 npm 支援團隊雙方的電子郵件,把包拿回來再重新發布。
- 同一個名字下,什麼是“官方”包,什麼是使用者/社群包?我們收到了有些人使用錯誤的名字或者非官方包的問題報告,原因是他們以為這是 Babel 的一部分。關於這個一個例子是有一份報告說
babel-env
重寫了他們的.babelrc
檔案。他們花了一些時間才意識到它不是babel-preset-env
。
所以,這已經很清楚了,我們應該使用 scoped packages,並且,不論什麼,我們應該快一些完成它!
scoped name 更改的例子:
babel-cli
->@babel/cli
babel-core
->@babel/core
babel-preset-env
->@babel/preset-env
重新命名:-proposal-
現在任何提案都將被以 -proposal-
命名來標記他們還沒有在 JavaScript 官方之內。
所以 @babel/plugin-transform-class-properties
變成 @babel/plugin-proposal-class-properties
,當它進入 Stage 4 後,我們會把它命名回去。
重新命名:把外掛名字裡的年份去掉
之前的 plugins 名字裡有年份,但是現在似乎並不是必須的。
所以 @babel/plugin-transform-es2015-classes
變成 @babel/plugin-transform-classes
。
因為年份只是用於 es3/es2015,我們沒有從 es2016 或 es2017 裡改變任何東西。無論怎樣,我們將那些 presets 設定為 preset-env,並且,對於字面模板調整,我們只是簡單地把它新增到字面模板轉換裡。
Peer dependencies 和 integrations
我們介紹一下 @babel/core
上的 peer dependencies,用於所有的 plugins(@babel/plugin-class-properties
),presets(@babel/preset-env
)和 top level packages(@babel/cli
,babel-loader
)
peerDependencies 是被你的程式碼期望使用的依賴,與只被用於實現細節的 dependencies 相反。- Stijn de Witt 在 StackOverflow 上的回答
babel-loader
在 babel-core
上 已經有一個 peerDependency
了,所以這個只是把它變成了 @babel/core
。這個改變阻止了人們嘗試去安裝這些 Babel 包的錯誤版本。
對於在 babel-core
已經有 peerDependency
和沒有準備好主要更新(因為改變 peer dependency 是一個 breaking change)的工具,我們已經發布了一個新版本的 babel-core
來橋接新版本上的改變 babel-core@7.0.0-bridge.0。想獲得更多地資訊,檢視 這個議題。
類似的,像 gulp-babel
,rollup-plugin-babel
等等的包都曾經把 babel-core
作為一個 dependency。現在,這些將僅僅在 @babel/core
上有 peerDependency
。因為這個,這些包沒有必要在 @babel/core
API 改變的時候升級主要版本
#5224:獨立釋出 experimental packages
我在 Babel 的狀態 的 Versioning
部分提到過這個。這裡是 Github Issue。
你可能記得在 Babel 6 之後,Babel 變成了一個擁有它自己的生態系統的一套 npm 包,這個生態系統有 custom presets 和 plugins。
然而,從那以後,我們一直使用一個 “fixed/synchronized” 的版本系統(所以沒有包是在 v7.0 或者是以上的)。當我們做一個新的釋出,例如 v6.23.0
,只有原始碼更新過的包才會隨著新版本一起被髮布。其餘的包保持原樣。這在實踐中大多是管用的,因為我們在我們的包中使用了 ^
。
不幸的是,當一個包需要釋出一個主版本時,這種系統需要為所有的包都釋出一個。這要不意味著我們做了很多小的 breaking changes(不必要的),要不就意味著我們我們打包了很多 breaking changes 到一個釋出中。相反,我們想把 experimental packages(Stage 0 等等)和 所有其他的(es2015)區分開。
因為這個,當規範變化時,我們打算將所有的 experimental proposal plugins 的主版本迭代,而不只是等待去更新所有的 Babel。所以,任何 Stage 4 之前的東西都將以主版本迭代的形式開放給 breaking changes。Stage presets 自身也是一樣的(如果我們不把他們整個扔掉)。
與這個隨之而來的是我們要求 TC39 提案使用 -proposal-
的名字的決定,我們將對外掛及其所屬的 preset 做一個主版本迭代(而不是隻做一個可能讓人失望的補丁版本)。然後,我們將需要棄用舊的版本,並且建立一個不管規範變成什麼樣子都將自動更新使人們保持最新的框架(並使他們不被什麼東西卡住。我們沒有像 decorators 那麼幸運。)。
.babelrc
中的 env
選項沒有沒棄用
env
中的配置被賦予了比根配置項更高的優先順序。並且現在根據它們的標識來合併,而不是隻是同時使用 plugins 和 presets 這種奇怪的實現,所以現在我們可以這樣做:
{ presets: [ ['env', { modules: false}], ], env: { test: { presets: [ 'env' ], } },}
複製程式碼
有了 BABEL_ENV=test
,它將使用沒有選項的 test 中的配置來替換根環境配置。
支援 [class A extends Array](https://twitter.com/left_pad/status/940723982638157829)
(最早的警告)
Babel 將自動包含像 Array
, Error
和 HTMLElement
的原生 built-ins,所以做這個會在編譯 classes 的時候生效。
速度
- 很多來自 v8 團隊 bmeurer 的修復!
- 通過 web-tooling-benchmark twitter.com/left_pad/st… 提速 60%
preset-env:"useBuiltins":"usage"
babel-preset-env
介紹了編譯語法到不同的目標的想法。它同時介紹了通過 useBuiltIns
選項,只為目標新增不支援的 polyfills 的能力。
所以有了這個選項,一些事情比如:
import "babel-polyfill";
複製程式碼
能夠變成
import "core-js/modules/es7.string.pad-start";import "core-js/modules/es7.string.pad-end";// ...
複製程式碼
如果目標環境恰巧支援 padStart
或 padEnd
之外的 polyfills。
但是為了使它更好,我們應該僅僅引入程式碼庫自身“使用”的 polyfills。如果它在程式碼中沒有使用 padStart
,為什麼引入?
"useBuiltins": "usage"
是我們解決那個想法的第一次嘗試。它在每個檔案的頭部執行了一個引入,但是隻有在找到它在程式碼中被使用的時候才新增引用。這個實現意味著我們能夠為應用引入最小量的,必須的 polyfills(並且只有在目標環境不支援它的時候)。
所以如果你在程式碼中使用 Promise
,它將在檔案的頭部引入它(如果你的目標不支援它)。如果它是重複的,Bundlers 將會刪除重複的部分,所以這個實現不會造成 polyfills 的多重引用。
import "core-js/modules/es6.promise";var a = new Promise();
複製程式碼
import "core-js/modules/es7.array.includes";[].includesa.includes
複製程式碼
通過型別推斷我們能知道一個像 .includes
的例項是否是一個 array。在邏輯變得更好之前,得到一個 false positive 是可以的,因為它仍舊是優於之前引入整個 polyfill 的。
其他更新
[babel-template](https://github.com/babel/babel/blob/master/packages/babel-template)
更快並且用起來更簡單- regenerator 在 MIT 證照下發布 - 它是用來編譯 generators/async 的依賴
- 通過 #6952 的
modules-commonjs
plugin 的 “lazy” 選項 - 你現在能在 .babelrc 中使用
envName: "something"
或者 在命令列輸入babel --envName=something
來取代必須使用process.env.BABEL_ENV
或process.env.NODE_ENV
["transform-for-of", { "assumeArray": true }]
來使所有的 for-of 迴圈按照規則陣列輸出- 為了 preset-env #6831,在鬆散模式中排除
transform-typeof-symbol
- 實現 PR 來獲得更好的語法錯誤訊息
釋出之前需要做的
- Handle
[.babelrc](https://github.com/babel/babel/issues/6766)
lookup(想在第一次 RC 釋出之前完成) - “overrides” support:基於全域性模式的差異配置
- babel-core 中的快取和無效邏輯
- 圍繞外部 helpers 的更好的 story。
- 對於迭代和處理 independently 對於 helpers 的獨立性,實現它或者是有一個成熟的計劃,所以我們不是明確的與 core-js 2 或 3 繫結。人們可能有東西依賴於一個或者其他,並且不想同時載入兩者太多。
- 不管是一個working decorator 的實現,或者是 functional legacy 的實現,在 7.x’s 的有效期內,有一個明確的路徑實現當前標準的行為。
感謝
向我們的志願者團隊致意:
Logan 已經很努力的推進修復很多我們關於配置及更多的核心問題。他就是那個做了所有困難工作的人。
Brian 已經接管很多 preset-env 的維護工作,無論以前我做過什麼 ?
Daniel 一直在我們需要的幫助的時候介入,不管是維護 babel-loader 或是幫助遷移 babylon/babel-preset-env 庫。同樣的,Nicolo,Sven,Artem 和 Diogo,在上一年幫助做了更多。
我真的很期待一個釋出(我也累了 - 已經快一年了 ?)。但是我也不想催促任何東西,因為在這個版本中已經有很多的起伏了。我確實學到了很多東西,我相信團隊的其他成員也是如此。
如果我今年學到了什麼,我應該真正的聽從這個建議而不只是寫下來。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。