[譯]babel版本7.7.0功能更新
7.7.0發(fā)布:錯誤恢復(fù)和TypeScript 3.7
2019年11月5日
今天我們將發(fā)布Babel 7.7.0!
此版本包括新的分析器功能,如頂級的await( await x(),第3階段)和Flow enum聲明(Flow的建議)?,F(xiàn)在,@babel/parser可以選擇從某些語法錯誤中恢復(fù)!
我們還增加了對TypeScript 3.7的支持:Babel可以解析和轉(zhuǎn)換帶有類型注釋的私有類字段,使用declare關(guān)鍵字定義的公共類字段注釋,類型聲明函數(shù)簽名和enum聲明中的模板文字。
babel現(xiàn)在接受了三個新的配置文件:babel.config.json,babel.config.cjs和.babelrc.cjs,它們的行為和babel.config.js和.babelrc.js文件一樣。
最后,Babel 7.7.0使用的內(nèi)存比7.6.0少20%。
可以在GitHub上閱讀整個變更日志。
頂級await解析(#10449)
頂級await proposal允許您在模塊中await promise,就像它們被包裝在一個大型異步函數(shù)中一樣。例如,這對于有條件地加載依賴項或執(zhí)行應(yīng)用程序初始化很有用:
// Dynamic dependency path
const strings = await import(`./i18n/${navigator.language}.mjs`);
// Resource initialization
const connection = await dbConnector();
@babel/parser自版本7.0.0起,已支持await通過該allowAwaitOutsideFunction選項使用異步功能之外的功能。
版本7.7.0引入了一個新的topLevelAwait解析器插件,該插件有一些主要區(qū)別:
- 根據(jù)
await提案的要求,它僅允許頂層內(nèi)部模塊,而不允許內(nèi)部腳本。這是必需的,因為基于同步腳本的模塊系統(tǒng)(例如CommonJS)無法支持異步依賴關(guān)系。 - 使用它可以檢測正確的
sourceType時間sourceType: "unambiguous"。請注意,由于await在腳本中是有效的標(biāo)識符,因此許多看起來似乎毫不含糊的構(gòu)造實際上是模棱兩可的,Babel會將它們解析為腳本。例如,await -1可以是一個waiting表達式的waiting-1,或者是await和之間的差異1。
如果@babel/parser直接使用,則可以啟用topLevelAwait插件:
parser.parse(inputCode, {
plugins: ["topLevelAwait"]
});
我們還創(chuàng)建了@babel/plugin-syntax-top-level-await軟件包,您可以將其添加到Babel配置中:
// babel.config.js
module.exports = {
plugins: [
"@babel/plugin-syntax-top-level-await"
]
}
請注意,頂層使用await假定為模塊捆綁程序中的支持。Babel本身并沒有進行轉(zhuǎn)換:如果您使用匯總,則可以啟用該experimentalTopLevelAwait選項,而webpack 5支持該experiments.topLevelAwait選項。
從此版本開始,如果支持,它將自動啟用@babel/preset-env。
解析器錯誤恢復(fù)(#10363)
像許多其他JavaScript解析器一樣,@babel/parser每當(dāng)遇到某些無效語法時,都會引發(fā)錯誤。這種行為對于Babel來說效果很好,因為要將JavaScript程序轉(zhuǎn)換為另一個程序,我們必須首先確保輸入有效。
鑒于Babel的流行,還有許多其他工具依賴@babel/parser:首先是babel-eslint 和 Prettier。對于這兩種工具,在出現(xiàn)第一個錯誤時無法使用時都不理想。
考慮以下代碼,由于重復(fù)proto屬性,該代碼無效:
let a = {
__proto__: x,
__proto__: y
}
let a = 2;
ESLint和Prettier當(dāng)前的工作流程如下:
- Prettier無法格式化文件
- ESLint報告
Redefinition of __proto__ property解析器錯誤 - 您刪除第二個
__proto__屬性 - Prettier無法格式化文件
- ESLint報告
Identifier 'a' has already been declared錯誤 - 您刪除第二個
let關(guān)鍵字 - Prettier 格式化文件格式
如果它更像這樣會更好嗎?
- Prettier格式化文件格式
- ESLint報告兩個錯誤:
Redefinition of __proto__ property和Identifier 'a' has already been declared - 您刪除第二個
__proto__屬性和第二個let關(guān)鍵字
在這個版本中,我們添加一個新的選項@babel/parser:errorRecovery。設(shè)置為true時,生成的AST將具有一個errors屬性,其中包含所有@babel/parser能夠從以下其中恢復(fù)的錯誤:
const input = `
let a = {
__proto__: x,
__proto__: y
}
let a = 2;
`;
parser.parse(input); // Throws "Redefinition of __proto__ property"
const ast = parser.parse(input, { errorRecovery: true });
ast.errors == [
SyntaxError: "Redefinition of __proto__ property",
SyntaxError: "Identifier 'a' has already been declared",
];
@babel/parser仍然可以拋出,因為并非每個錯誤當(dāng)前都可以恢復(fù)。我們將繼續(xù)改善這些情況!
新的配置文件擴展名(#10501,#10599)
Babel 6僅支持一個配置文件:.babelrc,其內(nèi)容必須使用JSON指定。
Babel 7更改了.babelrcs 的含義,并引入了兩個新的配置文件:babel.config.js和.babelrc.js(您可以在docs中了解它們之間的區(qū)別)。我們使用JavaScript添加了配置文件,以允許在啟用/禁用插件/選項時定義自己的邏輯。
但是,JSON文件的一大好處是更易于緩存。兩次調(diào)用時,同一個JavaScript文件可以產(chǎn)生不同的值,而JSON文件可以保證始終對同一對象求值。此外,JSON配置易于序列化,而無法使用隱式數(shù)據(jù)或關(guān)系序列化JavaScript值(如函數(shù)或JavaScript對象)。
請注意,Babel在使用基于JavaScript的配置時也會緩存轉(zhuǎn)換,但是必須評估config文件(以便知道緩存是否仍然有效),并手動配置緩存。
由于這些原因,Babel 7.7.0引入了對新配置文件的支持:babel.config.json,其行為與相同babel.config.js。
我們還添加了對兩個不同配置文件的支持:babel.config.cjs和.babelrc.cjs,在中使用node的"type": "module"選項時必須使用package.json(因為Babel在配置文件中不支持ECMAScript模塊)。除了這種"type": "module"差異之外,它們的行為與babel.config.js和完全相同.babelrc.js。
TypeScript 3.7(#10543,#10545)
TypeScript 3.7 RC支持可選的鏈接,無效的合并運算符,斷言函數(shù),類型字段聲明以及許多其他與類型相關(guān)的功能。
自7.0.0起,通過和都支持Babel中的可選鏈(a?.b)和無效合并(a ?? b)。@babel/plugin-proposal-optional-chaining和@babel/plugin-proposal-nullish-coalescing-operator
在Babel 7.7.0中,您現(xiàn)在可以在斷言函數(shù)和declare類字段中使用:
function assertString(x): assert x is string {
if (typeof x !== "string") throw new Error("It must be a string!");
}
class Developer extends Person {
declare usingBabel: boolean;
}
為了避免重大更改,我們推出了支持declare在一個標(biāo)志背后類字段:"allowDeclareFields",雙方支持@babel/plugin-transform-typescript和@babel/preset-typescript。這可能會成為默認行為,因此建議您遷移配置以使用它:
{
"presets": [
["@babel/preset-typescript", {
"allowDeclareFields": true
}]
]
}
使用對象在已編譯的JSX中傳播(#10572)
在JSX元素中使用傳播屬性時,Babel默認情況下會注入運行時幫助程序:
<a x {...y} />
//
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
React.createElement("a", _extends({
x: true
}, y));
2016年,隨著對本地ES6的支持得到改善,我們添加了一個useBuiltIns選項,@babel/plugin-transform-react-jsx該選項允許編譯后的輸出直接使用Object.assign并刪除多余的代碼:
<a x {...y} />
//
React.createElement("a", Object.assign({
x: true
}, y));
但是,鑒于對對象傳播的本地支持,它使我們能夠生成更多優(yōu)化的代碼:
<a x {...y} />
//
React.createElement("a", { x: true, ...y });
您可以使用或啟用該useSpread選項:@babel/preset-react@babel/plugin-transform-react-jsx
{
presets: [
["@babel/react", { useSpread: true }]
]
}
內(nèi)存使用率改進(#10480)
從一開始,我們就一直在努力(#433,#3475,#7028等)來提高性能。Babel 7.7.0現(xiàn)在使用的內(nèi)存減少了20%,與7.6.0相比,轉(zhuǎn)換大型文件的速度提高了8%。
為了獲得這些結(jié)果,我們優(yōu)化了在生存期NodePath對象(用于包裝每個AST節(jié)點)中完成的不同操作:
- 現(xiàn)在我們避免初始化一些很少使用的對象屬性,直到需要它們?yōu)橹?,這使我們避免Object.create(null)為幾乎每個AST節(jié)點分配資源。
- 通過用bookkeeping替換一些不常見的屬性,從而@babel/traverse可以跳過更新它們,我們減少了每次訪問單個節(jié)點的工作量。
- 我們通過將表示節(jié)點遍歷(即,跳過,停止或刪除)的狀態(tài)的幾個布爾屬性壓縮到位數(shù)組中來優(yōu)化內(nèi)存使用。
所有這些改進加起來在轉(zhuǎn)換性能和內(nèi)存使用方面存在以下差異:
您也可以檢出上面圖表的原始數(shù)據(jù)。如果您想了解更多有關(guān)此主題的信息,則可以閱讀Jùnliàng關(guān)于他為獲得這些改進所做的更改的詳細文章!