自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

[[383627]]

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

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

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

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

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

Babel是什么

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

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


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

  1. 將Input Code解析為AST(抽象語(yǔ)法樹(shù)),這一步稱為parsing
  2. 編輯AST,這一步稱為transforming
  3. 將編輯后的AST輸出為Output Code,這一步稱為printing

Babel倉(cāng)庫(kù)[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)第三步

要了解第二步,我們需要簡(jiǎn)單了解下AST。

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

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

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

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


在babel-core內(nèi)部:

  • babel-traverse可以通過(guò)「深度優(yōu)先」的方式遍歷AST樹(shù)
  • 對(duì)于遍歷到的每條路徑,babel-types提供用于修改AST節(jié)點(diǎn)的節(jié)點(diǎn)類型數(shù)據(jù)

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


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

Babel的上層能力

基于Babel對(duì)JS代碼的編譯處理能力,Babel最常見(jiàn)的上層能力為:

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

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

polyfill

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

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

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

答案是:core-js

core-js簡(jiǎn)介

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

  • 一直到ES2021的polyfill
  • promise、symbols、iterators等一些特性的實(shí)現(xiàn)
  • ES提案中的特性實(shí)現(xiàn)
  • 跨平臺(tái)的WHATWG / W3C特性,比如URL

[[383630]]

core-js作者Denis Pushkarev

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

  • core-js-builder
  • core-js-bundle
  • core-js-compat
  • core-js-pure
  • core-js

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

core-js

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

  1. import 'core-js/features/array/from';  
  2. import 'core-js/features/array/flat';  
  3. import 'core-js/features/set';         
  4. import 'core-js/features/promise';     
  5.  
  6. Array.from(new Set([1, 2, 3, 2, 1]));          // => [1, 2, 3] 
  7. [1, [2, 3], [4, [5]]].flat(2);                 // => [1, 2, 3, 4, 5] 
  8. Promise.resolve(32).then(x => console.log(x)); // => 32 

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

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

core-js-pure

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

  1. import from from 'core-js-pure/features/array/from'
  2. import flat from 'core-js-pure/features/array/flat'
  3. import Set from 'core-js-pure/features/set'
  4. import Promise from 'core-js-pure/features/promise'
  5.  
  6. from(new Set([1, 2, 3, 2, 1]));                // => [1, 2, 3] 
  7. flat([1, [2, 3], [4, [5]]], 2);                // => [1, 2, 3, 4, 5] 
  8. Promise.resolve(32).then(x => console.log(x)); // => 32 

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

core-js-compat

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

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

[[383631]]

Browserslist

比如:

  1. "browserslist": [ 
  2.     "not IE 11"
  3.     "maintained node versions" 
  4.   ] 

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

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

@babel/polyfill可以看作是:core-js加regenerator-runtime。

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

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

  • Babel v7.4.0[5]開(kāi)始,@babel/polyfill被廢棄了,可以直接引用core-js與regenerator-runtime替代

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

preset的含義

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

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

  1. const babel = code => code; 

其通過(guò)plugin對(duì)外提供介入babel-core的能力,類似webpack的plugin對(duì)外提供介入webpack編譯流程的能力。

plugin分為幾類:

  • @babel/plugin-syntax-*語(yǔ)法相關(guān)插件,用于新的語(yǔ)法支持。比如babel-plugin-syntax-decorators[6]提供decorators的語(yǔ)法支持
  • @babel/plugin-proposal-*用于ES提案的特性支持,比如babel-plugin-proposal-optional-chaining是可選鏈操作符特性支持
  • @babel/plugin-transform-*用于轉(zhuǎn)換代碼,transform插件內(nèi)部會(huì)使用對(duì)應(yīng)syntax插件

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

@babel/preset-env

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

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

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

我們來(lái)依次看下。

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

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

  1. not IE 11 
  2. maintained node versions 

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

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

按使用情況的粒度

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

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

比如:

a.js:

  1. var a = new Promise(); 

b.js:

  1. var b = new Map(); 

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

a.js:

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

b.js:

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

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

a.js:

  1. var a = new Promise(); 

b.js:

  1. var b = new Map(); 

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

打開(kāi)babel playground[7],輸入:

  1. class App {} 

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

  1. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 
  2.  
  3. var App = function App() { 
  4.   "use strict"
  5.  
  6.   _classCallCheck(this, App); 
  7. }; 

其中_classCallCheck為輔助方法。

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

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

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

單純引入@babel/runtime還不行,因?yàn)锽abel不知道何時(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ù)開(kāi)發(fā)會(huì)接觸的Babel大家族成員。他們包括:

底層

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

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

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

中層

@babel/plugin-*

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

上層

@babel/preset-*

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

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

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

參考資料

[1]Babel倉(cāng)庫(kù):

https://github.com/babel/babel/tree/main/packages

[2]AST explorer:

https://astexplorer.net

/[3]core-js倉(cāng)庫(kù):

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

 

責(zé)任編輯:姜華 來(lái)源: 魔術(shù)師卡頌
相關(guān)推薦

2021-03-16 14:29:05

postCSS前端架構(gòu)師

2013-10-09 09:32:58

2024-10-24 23:40:34

2016-11-07 13:31:24

2017-09-22 11:18:19

2009-12-17 17:40:45

架構(gòu)師

2017-03-30 16:41:07

互聯(lián)網(wǎng)

2020-06-28 14:15:52

前端架構(gòu)師互聯(lián)網(wǎng)

2018-03-15 13:14:59

思科網(wǎng)絡(luò)技術(shù)智慧系統(tǒng)

2018-02-10 11:24:39

Python數(shù)據(jù)程序

2012-09-06 13:12:41

架構(gòu)師ArchSummit

2020-07-22 22:10:34

互聯(lián)網(wǎng)物聯(lián)網(wǎng)IOT

2012-10-22 10:01:45

TD-LTETD-LTE頻譜TDD頻譜

2021-05-14 05:26:25

前端架構(gòu)開(kāi)發(fā)

2012-06-17 12:58:04

架構(gòu)師架構(gòu)

2020-11-11 09:37:56

芯片

2022-01-04 10:19:23

架構(gòu)運(yùn)維技術(shù)

2020-08-24 08:50:12

架構(gòu)師TL技術(shù)

2009-12-18 10:22:50

Ray Ozzie架構(gòu)師

2013-04-27 13:36:52

Chrome
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)