成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

解剖Babel - 向前端架構師邁出一小步

開發 前端
本文會從Babel的核心功能出發,一步步揭開Babel大家族的神秘面紗,向前端架構師邁出一小步。

[[383627]]

 當聊到Babel的作用,很多人第一反應是:用來實現API polyfill。

事實上,Babel作為前端工程化的基石,作用遠不止這些。

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

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

本文會從Babel的核心功能出發,一步步揭開Babel大家族的神秘面紗,向前端架構師邁出一小步。

Babel是什么

  • Babel 是一個 JavaScript 編譯器。

作為JS編譯器,Babel接收輸入的JS代碼,經過內部處理流程,最終輸出修改后的JS代碼。


在Babel內部,會執行如下步驟:

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

Babel倉庫[1]的源代碼,可以發現:Babel是一個由幾十個項目組成的Monorepo。

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

在babel-core內部,更細致的講:

  • babel-parser實現第一步
  • babel-generator實現第三步

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

AST的結構

進入AST explorer[2],選擇@babel/parser作為解析器,在左側輸入:

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

可以解析出如下結構的AST,他是JSON格式的樹狀結構:


在babel-core內部:

  • babel-traverse可以通過「深度優先」的方式遍歷AST樹
  • 對于遍歷到的每條路徑,babel-types提供用于修改AST節點的節點類型數據

所以,整個Babel底層編譯能力由如下部分構成:


當我們了解Babel的底層能力后,接下來看看基于這些能力,上層能實現什么功能?

Babel的上層能力

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

  • polyfill
  • DSL轉換(比如解析JSX)
  • 語法轉換(比如將高級語法解析為當前可用的實現)

由于篇幅有限,這里僅介紹polyfill與「語法轉換」相關功能。

polyfill

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

使用@babel/polyfill或@babel/preset-env可以實現高級語法的降級實現以及API的polyfill。

從上文我們知道,Babel本身只是JS的編譯器,以上兩者的轉換功能是誰實現的呢?

答案是:core-js

core-js簡介

core-js是一套模塊化的JS標準庫,包括:

  • 一直到ES2021的polyfill
  • promise、symbols、iterators等一些特性的實現
  • ES提案中的特性實現
  • 跨平臺的WHATWG / W3C特性,比如URL

[[383630]]

core-js作者Denis Pushkarev

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

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

我們介紹其中幾個庫:

core-js

core-js提供了polyfill的核心實現。

  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會污染全局命名空間和對象原型。

比如上例中修改了Array的原型以支持數組實例的flat方法。

core-js-pure

core-js-pure提供了獨立的命名空間:

  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 

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

core-js-compat

core-js-compat根據Browserslist維護了不同宿主環境、不同版本下對應需要支持特性的集合。

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

[[383631]]

Browserslist

比如:

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

代表:非IE11的版本以及所有Node.js基金會維護的版本。

@babel/polyfill與core-js關系

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

  • regenerator-runtime是generator以及async/await的運行時依賴

單獨使用@babel/polyfill會將core-js全量導入,造成項目打包體積過大。

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

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

preset的含義

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

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

  1. const babel = code => code; 

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

plugin分為幾類:

  • @babel/plugin-syntax-*語法相關插件,用于新的語法支持。比如babel-plugin-syntax-decorators[6]提供decorators的語法支持
  • @babel/plugin-proposal-*用于ES提案的特性支持,比如babel-plugin-proposal-optional-chaining是可選鏈操作符特性支持
  • @babel/plugin-transform-*用于轉換代碼,transform插件內部會使用對應syntax插件

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

@babel/preset-env

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

這里的「按需」,分為兩個粒度:

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

我們來依次看下。

宿主環境的粒度

當我們按如下參數在項目目錄下配置browserslist文件(或在@babel/preset-env的targets屬性內設置,或在package.json的browserslist屬性中設置):

  1. not IE 11 
  2. maintained node versions 

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

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

按使用情況的粒度

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

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

比如:

a.js:

  1. var a = new Promise(); 

b.js:

  1. var b = new Map(); 

當宿主環境不支持promise與Map時,輸出的文件為:

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(); 

當宿主環境支持這兩個特性時,輸出的文件為:

a.js:

  1. var a = new Promise(); 

b.js:

  1. var b = new Map(); 

進一步優化打包體積

打開babel playground[7],輸入:

  1. class App {} 

會發現編譯出的結果為:

  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為輔助方法。

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

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

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

單純引入@babel/runtime還不行,因為Babel不知道何時引用@babel/runtime中的「輔助方法」

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

這個插件會在編譯時將所有使用「輔助方法」的地方從「自己維護一份」改為從@babel/runtime中引入。

所以我們需要將@babel/plugin-transform-runtime置為devDependence,因為他在編譯時使用。

將@babel/runtime置為dependence,因為他在運行時使用。

總結

本文從底層向上介紹了前端日常業務開發會接觸的Babel大家族成員。他們包括:

底層

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

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

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

中層

@babel/plugin-*

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

上層

@babel/preset-*

日常開發會使用的插件集合。

對于立志成為前端架構師的同學,Babel是前端工程化的基石,學懂、會用他是很有必要的。

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

參考資料

[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

 

責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2021-03-16 14:29:05

postCSS前端架構師

2013-10-09 09:32:58

2016-11-07 13:31:24

2024-10-24 23:40:34

2017-09-22 11:18:19

2009-12-17 17:40:45

架構師

2020-06-28 14:15:52

前端架構師互聯網

2018-03-15 13:14:59

思科網絡技術智慧系統

2017-03-30 16:41:07

互聯網

2018-02-10 11:24:39

Python數據程序

2020-07-22 22:10:34

互聯網物聯網IOT

2012-10-22 10:01:45

TD-LTETD-LTE頻譜TDD頻譜

2012-09-06 13:12:41

架構師ArchSummit

2020-11-11 09:37:56

芯片

2011-11-28 14:59:47

華為

2023-09-18 14:39:02

2017-03-07 15:54:13

華為

2013-04-27 13:36:52

Chrome

2012-06-17 12:58:04

架構師架構

2021-05-14 05:26:25

前端架構開發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产97在线 | 日韩 | 蜜桃视频在线观看www社区 | 夜夜爽99久久国产综合精品女不卡 | 91精品国产高清久久久久久久久 | 国产精品久久久久久久久久久久久久 | 国产免费va | 国内精品在线视频 | 激情影院久久 | 福利成人| 自拍偷拍小视频 | 国产av毛片 | 精品视频在线一区 | 伊人操| 亚洲国产在 | 久草青青 | 久久成人av| 九九热精品在线视频 | 一区二区三区四区视频 | 日本大香伊一区二区三区 | 波多野结衣先锋影音 | 视频一区 国产精品 | 亚洲欧美日韩在线 | 国产精品一区二区在线 | 日韩在线不卡视频 | 九七午夜剧场福利写真 | 欧美在线一区视频 | 伦理片97| 91久久精品日日躁夜夜躁欧美 | 精品国产31久久久久久 | 久久精品色欧美aⅴ一区二区 | 亚洲一区二区三区在线播放 | 啪啪免费网站 | 色综合九九 | 日韩美香港a一级毛片免费 国产综合av | 亚洲日韩第一页 | 国产成人精品一区二区三区在线 | 国产精品一区2区 | 99riav3国产精品视频 | 日韩精品在线一区 | 欧美日韩手机在线观看 | 黄色国产在线视频 |