[譯]babel版本7.7.0功能更新

[譯]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)前的工作流程如下:

  1. Prettier無法格式化文件
  2. ESLint報告Redefinition of __proto__ property解析器錯誤
  3. 您刪除第二個__proto__屬性
  4. Prettier無法格式化文件
  5. ESLint報告Identifier 'a' has already been declared錯誤
  6. 您刪除第二個let關(guān)鍵字
  7. Prettier 格式化文件格式

如果它更像這樣會更好嗎?

  1. Prettier格式化文件格式
  2. ESLint報告兩個錯誤:Redefinition of __proto__ propertyIdentifier 'a' has already been declared
  3. 您刪除第二個__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é)點)中完成的不同操作:

  1. 現(xiàn)在我們避免初始化一些很少使用的對象屬性,直到需要它們?yōu)橹?,這使我們避免Object.create(null)為幾乎每個AST節(jié)點分配資源。
  2. 通過用bookkeeping替換一些不常見的屬性,從而@babel/traverse可以跳過更新它們,我們減少了每次訪問單個節(jié)點的工作量。
  3. 我們通過將表示節(jié)點遍歷(即,跳過,停止或刪除)的狀態(tài)的幾個布爾屬性壓縮到位數(shù)組中來優(yōu)化內(nèi)存使用。

所有這些改進加起來在轉(zhuǎn)換性能和內(nèi)存使用方面存在以下差異:

您也可以檢出上面圖表的原始數(shù)據(jù)。如果您想了解更多有關(guān)此主題的信息,則可以閱讀Jùnliàng關(guān)于他為獲得這些改進所做的更改的詳細文章!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容