<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          解剖Babel —— 向前端架構(gòu)師邁出一小步

          共 7120字,需瀏覽 15分鐘

           ·

          2021-02-28 21:12

          當(dāng)聊到Babel的作用,很多人第一反應(yīng)是:用來實(shí)現(xiàn)API polyfill

          事實(shí)上,Babel作為前端工程化的基石,作用遠(yuǎn)不止這些。

          作為一個(gè)龐大的家族,Babel生態(tài)中有很多概念,比如:preset、plugin、runtime等。

          這些概念使初學(xué)者對Babel望而生畏,對其理解也止步于webpackbabel-loader配置。

          本文會(huì)從Babel的核心功能出發(fā),一步步揭開Babel大家族的神秘面紗,向前端架構(gòu)師邁出一小步。

          Babel是什么

          Babel 是一個(gè) JavaScript 編譯器。

          作為JS編譯器,Babel接收輸入的JS代碼,經(jīng)過內(nèi)部處理流程,最終輸出修改后的JS代碼。

          Babel內(nèi)部,會(huì)執(zhí)行如下步驟:

          1. Input Code解析為AST(抽象語法樹),這一步稱為parsing

          2. 編輯AST,這一步稱為transforming

          3. 將編輯后的AST輸出為Output Code,這一步稱為printing

          Babel倉庫[1]的源代碼,可以發(fā)現(xiàn):Babel是一個(gè)由幾十個(gè)項(xiàng)目組成的Monorepo。

          其中babel-core提供了以上提到的三個(gè)步驟的能力。

          babel-core內(nèi)部,更細(xì)致的講:

          • babel-parser實(shí)現(xiàn)第一步

          • babel-generator實(shí)現(xiàn)第三步

          要了解第二步,我們需要簡單了解下AST。

          AST的結(jié)構(gòu)

          進(jìn)入AST explorer[2],選擇@babel/parser作為解析器,在左側(cè)輸入:

          const name = ['ka''song'];

          可以解析出如下結(jié)構(gòu)的AST,他是JSON格式的樹狀結(jié)構(gòu):

          babel-core內(nèi)部:

          • babel-traverse可以通過「深度優(yōu)先」的方式遍歷AST

          • 對于遍歷到的每條路徑,babel-types提供用于修改AST節(jié)點(diǎn)的節(jié)點(diǎn)類型數(shù)據(jù)

          所以,整個(gè)Babel底層編譯能力由如下部分構(gòu)成:

          當(dāng)我們了解Babel的底層能力后,接下來看看基于這些能力,上層能實(shí)現(xiàn)什么功能?

          Babel的上層能力

          基于BabelJS代碼的編譯處理能力,Babel最常見的上層能力為:

          • polyfill

          • DSL轉(zhuǎn)換(比如解析JSX

          • 語法轉(zhuǎn)換(比如將高級語法解析為當(dāng)前可用的實(shí)現(xiàn))

          由于篇幅有限,這里僅介紹polyfill「語法轉(zhuǎn)換」相關(guān)功能。

          polyfill

          作為前端,最常見的Babel生態(tài)的庫想必是@babel/polyfill@babel/preset-env。

          使用@babel/polyfill@babel/preset-env可以實(shí)現(xiàn)高級語法的降級實(shí)現(xiàn)以及APIpolyfill。

          從上文我們知道,Babel本身只是JS的編譯器,以上兩者的轉(zhuǎn)換功能是誰實(shí)現(xiàn)的呢?

          答案是:core-js

          core-js簡介

          core-js是一套模塊化的JS標(biāo)準(zhǔn)庫,包括:

          • 一直到ES2021polyfill

          • promise、symbolsiterators等一些特性的實(shí)現(xiàn)

          • ES提案中的特性實(shí)現(xiàn)

          • 跨平臺(tái)的WHATWG / W3C特性,比如URL

          core-js作者Denis Pushkarev

          core-js倉庫[3]看到,core-js也是由多個(gè)庫組成的Monorepo,包括:

          • core-js-builder

          • core-js-bundle

          • core-js-compat

          • core-js-pure

          • core-js

          我們介紹其中幾個(gè)庫:

          core-js

          core-js提供了polyfill的核心實(shí)現(xiàn)。

          import 'core-js/features/array/from'
          import 'core-js/features/array/flat'
          import 'core-js/features/set';        
          import 'core-js/features/promise';    

          Array.from(new Set([12321]));          // => [1, 2, 3]
          [1, [23], [4, [5]]].flat(2);                 // => [1, 2, 3, 4, 5]
          Promise.resolve(32).then(x => console.log(x)); // => 32

          直接使用core-js會(huì)污染全局命名空間和對象原型。

          比如上例中修改了Array的原型以支持?jǐn)?shù)組實(shí)例的flat方法。

          core-js-pure

          core-js-pure提供了獨(dú)立的命名空間:

          import from from 'core-js-pure/features/array/from';
          import flat from 'core-js-pure/features/array/flat';
          import Set from 'core-js-pure/features/set';
          import Promise from 'core-js-pure/features/promise';

          from(new Set([12321]));                // => [1, 2, 3]
          flat([1, [23], [4, [5]]], 2);                // => [1, 2, 3, 4, 5]
          Promise.resolve(32).then(x => console.log(x)); // => 32

          這樣使用不會(huì)污染全局命名空間與對象原型。

          core-js-compat

          core-js-compat根據(jù)Browserslist維護(hù)了不同宿主環(huán)境、不同版本下對應(yīng)需要支持特性的集合。

          Browserslist[4]提供了不同瀏覽器、node版本下ES特性的支持情況

          Browserslist

          比如:

          "browserslist": [
              "not IE 11",
              "maintained node versions"
            ]

          代表:非IE11的版本以及所有Node.js基金會(huì)維護(hù)的版本。

          @babel/polyfill與core-js關(guān)系

          @babel/polyfill可以看作是:core-jsregenerator-runtime。

          regenerator-runtimegenerator以及async/await的運(yùn)行時(shí)依賴

          單獨(dú)使用@babel/polyfill會(huì)將core-js全量導(dǎo)入,造成項(xiàng)目打包體積過大。

          Babel v7.4.0[5]開始,@babel/polyfill被廢棄了,可以直接引用core-jsregenerator-runtime替代

          為了解決全量引入core-js造成打包體積過大的問題,我們需要配合使用@babel/preset-env。

          preset的含義

          在介紹@babel/preset-env前,我們先來了解preset的意義。

          初始情況下,Babel沒有任何額外能力,其工作流程可以描述為:

          const babel = code => code;

          其通過plugin對外提供介入babel-core的能力,類似webpackplugin對外提供介入webpack編譯流程的能力。

          plugin分為幾類:

          • @babel/plugin-syntax-*語法相關(guān)插件,用于新的語法支持。比如babel-plugin-syntax-decorators[6]提供decorators的語法支持

          • @babel/plugin-proposal-*用于ES提案的特性支持,比如babel-plugin-proposal-optional-chaining可選鏈操作符特性支持

          • @babel/plugin-transform-*用于轉(zhuǎn)換代碼,transform插件內(nèi)部會(huì)使用對應(yīng)syntax插件

          多個(gè)plugin組合在一起形成的集合,被稱為preset。

          @babel/preset-env

          使用@babel/preset-env,可以「按需」core-js中的特性打包,這樣可以顯著減少最終打包的體積。

          這里的「按需」,分為兩個(gè)粒度:

          • 宿主環(huán)境的粒度。根據(jù)不同宿主環(huán)境將該環(huán)境下所需的所有特性打包

          • 按使用情況的粒度。僅僅將使用了的特性打包

          我們來依次看下。

          宿主環(huán)境的粒度

          當(dāng)我們按如下參數(shù)在項(xiàng)目目錄下配置browserslist文件(或在@babel/preset-envtargets屬性內(nèi)設(shè)置,或在package.jsonbrowserslist屬性中設(shè)置):

          not IE 11
          maintained node versions

          會(huì)將「非IE11」「所有Node.js基金會(huì)維護(hù)的node版本」下需要的特性打入最終的包。

          顯然這是利用了剛才介紹的core-js這個(gè)Monorepo下的core-js-compat的能力。

          按使用情況的粒度

          更理想的情況是只打包我們使用過的特性。

          這時(shí)候可以設(shè)置@babel/preset-envuseBuiltIns屬性為usage。

          比如:

          a.js

          var a = new Promise();

          b.js

          var b = new Map();

          當(dāng)宿主環(huán)境不支持promiseMap時(shí),輸出的文件為:

          a.js

          import "core-js/modules/es.promise";
          var a = new Promise();

          b.js

          import "core-js/modules/es.map";
          var b = new Map();

          當(dāng)宿主環(huán)境支持這兩個(gè)特性時(shí),輸出的文件為:

          a.js

          var a = new Promise();

          b.js

          var b = new Map();

          進(jìn)一步優(yōu)化打包體積

          打開babel playground[7],輸入:

          class App {}

          會(huì)發(fā)現(xiàn)編譯出的結(jié)果為:

          function _classCallCheck(instance, Constructorif (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

          var App = function App({
            "use strict";

            _classCallCheck(this, App);
          };

          其中_classCallCheck為輔助方法。

          如果多個(gè)文件都使用了class特性,那么每個(gè)文件打包對應(yīng)的module中都將包含_classCallCheck。

          為了減少打包體積,更好的方式是:需要使用「輔助方法」module都從同一個(gè)地方引用,而不是自己維護(hù)一份。

          @babel/runtime包含了Babel所有「輔助方法」以及regenerator-runtime

          單純引入@babel/runtime還不行,因?yàn)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(145, 109, 213);font-weight: bolder;background-image: none;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;">Babel不知道何時(shí)引用@babel/runtime中的「輔助方法」。

          所以,還需要引入@babel/plugin-transform-runtime。

          這個(gè)插件會(huì)在編譯時(shí)將所有使用「輔助方法」的地方從「自己維護(hù)一份」改為從@babel/runtime中引入。

          所以我們需要將@babel/plugin-transform-runtime置為devDependence,因?yàn)樗诰幾g時(shí)使用。

          @babel/runtime置為dependence,因?yàn)樗谶\(yùn)行時(shí)使用。

          總結(jié)

          本文從底層向上介紹了前端日常業(yè)務(wù)開發(fā)會(huì)接觸的Babel大家族成員。他們包括:

          底層

          @babel/core(由@babel/parser、@babel/traverse@babel/types、@babel/generator等組成)

          他們提供了Babel編譯JS的能力。

          注:這里@babel/core為庫名,前文中babel-core為其在倉庫中對應(yīng)文件名

          中層

          @babel/plugin-*

          Babel對外暴露的API,使開發(fā)者可以介入其編譯JS的能力

          上層

          @babel/preset-*

          日常開發(fā)會(huì)使用的插件集合。

          對于立志成為前端架構(gòu)師的同學(xué),Babel是前端工程化的基石,學(xué)懂、會(huì)用他是很有必要的。

          能看到這里真不容易,給自己鼓鼓掌吧。

          希斯特利亞筆芯

          參考資料

          [1]

          Babel倉庫: https://github.com/babel/babel/tree/main/packages

          [2]

          AST explorer: https://astexplorer.net/

          [3]

          core-js倉庫: https://github.com/zloirock/core-js/tree/master/packages

          [4]

          Browserslist: https://github.com/browserslist/browserslist

          [5]

          Babel v7.4.0: https://babeljs.io/docs/en/babel-polyfill#docsNav

          [6]

          babel-plugin-syntax-decorators: https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-decorators

          [7]

          babel playground: https://babeljs.io/repl#?browsers=&build=&builtIns=false&spec=false&loose=false&code_lz=MYGwhgzhAECCAO9oG8C-Q&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=script&lineWrap=true&presets=env&prettier=false&targets=&version=7.13.7&externalPlugins=babel-plugin-transform-regenerator%406.26.0


          瀏覽 75
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中国久久一级片 | 最新午夜理论在线观看 | 中国老熟女~x88AV | 国产高清色情 | 亚洲第一成年人网站 |