大前端的自動化工廠(3)—— babel

来源:https://www.cnblogs.com/dashnowords/archive/2018/08/27/9537311.html
-Advertisement-
Play Games

一. 關於babel 是ES6+語法的編譯器,官方網址: "www.babeljs.io" ,用於將舊版本瀏覽器無法識別的語法和特性轉換成為ES5語法,使代碼能夠適用更多環境。 最初的 使用起來是非常方便的,幾乎僅使用少量的配置就可以使用,但隨著工具的快速升級和代碼架構的轉變, 已經裂變成非常多的部 ...



一. 關於babel

babel是ES6+語法的編譯器,官方網址:www.babeljs.io,用於將舊版本瀏覽器無法識別的語法和特性轉換成為ES5語法,使代碼能夠適用更多環境。

最初的babel使用起來是非常方便的,幾乎僅使用少量的配置就可以使用,但隨著工具的快速升級和代碼架構的轉變,babel已經裂變成非常多的部分,每個部分各司其職,這樣做的好處是可以縮小生產環境的正式包的代碼體積(因為可以按需引用)而加重了開發環境(開發階段需要引入更多碎片化的插件),但劣勢就是將其使用門檻提得非常高,對軟體架構不熟悉的開發者難以使用。

比如babel官方網站在webpack配置的章節,提及了babe-loader,babel-corebabel-preset-env三個插件,而當開發者在webpack中實際進行配置時除了上述三個基本插件外,又會遇到babel-polyfill,babel-runtime,babel-plugin-transform-runtime等等一系列插件,或許通過查看插件說明能夠理解插件的功能,但開發者卻很難判斷自己是否該使用這個功能或者什麼時候使用。

二. 基本需求推演

我們從工具設計的角度,通過問題推演的方式來看看babel的變化。

ES6標準推出時,瀏覽器還不能很好地支持,但ES6的許多特性和語法又很誘人,所以大家想了個辦法,那就是用ES6編寫代碼,然後出包的時候拿個工具轉換一下,變成能被更多瀏覽器識別的ES5語法不就行了麽,於是,Babel基本模型就出現了:

babel的功能被定義為編譯工具,那麼理論上來說它就可以使用編譯器的通用代碼框架,通過ASTparser --> traverse --> stringify 的步驟實現編譯功能,在關鍵的traverse環節,是需要一個規則集合的,可是轉碼所參考的ES6的標準並不是一個定案的標準,其中每一個特性都需要經過從stage0stage4這樣5個階段才能正式定稿,只有stage-2草案(draft)階段以上的特性才會在未來被支持,而處於這個階段以下的標準是有可能被廢的,如果一味地全部轉換,不僅會降低工具效率,也會為代碼未來的維護造成隱患。

那如果我們有一個工廠函數,接受數字0-4作為參數,然後返回所有經歷了stage-x的規則集(是ES6規則的子集)作為規則集合,那麼就可以在最終生成生產環境的代碼時減小代碼體積,假如在項目中通過babel_get_es6_by_stage(2)這樣一個函數返回了規則集,那麼正式代碼中就不需要stage-0stage-1的實現代碼了。基於以上的考慮,我們對Babel工具進行第一次功能剝離:

推演繼續,在對規則集進行了一次體積縮減後,我們得到了一個相對精簡的規則集,它包含了諸多新的語法和方法,如果直接使用那的確很爽,畢竟引入了一個工具後就可以毫無後顧之憂地使用新特性,但對於生產環境的代碼包來說,這種做法造成的代碼冗餘確是非常難以接受的。

用大家都熟悉的bootstrap為例,bootstrap.min.css的體積大約為120k,可你會發現很多人引入它完全是出於心裡慣性,而在最後僅僅使用了非常基礎的btn相關的樣式類,或者僅僅為了使用col-md-4這種響應式佈局的樣式,所有使用到的樣式可能只占了20k-30k的空間,但是卻不得不為項目引進一個120k大的庫,當然並不是所有的項目都會在意20k和120k之間的差別的。

那麼我們就需要一個能夠按更小粒度組合的方法babel_get_es6_by_rules([rule , ...]),讓使用者可以選擇自己所使用到的語法和方法,從而達到縮小引用庫體積的目的:

推演繼續進行。處理過相容性問題的開發者都知道,瀏覽器是存在版本區分的,許多特性在不同瀏覽器中的實現和表現都不一樣,對於ES6也是這樣,較高版本的瀏覽器對於ES6中的一些特性是已經逐步實現支持了的,如果我們的目標用戶所使用的運行環境對某些ES6特性已經提供了原生支持,或者目標用戶的運行環境根本就是由開發者直接封裝好的,那麼原先“一鍋端”的轉碼方式里就會存在很多沒有必要的部分。

比如你在規則集中選擇了對Class關鍵字來定義類這個特性進行轉碼,那麼babel就需要將其轉碼成為使用functionprototype的ES5的實現方式,但如果你的目標用戶全都是程式員,幾乎全都是使用高版本的chrome作為項目環境,那麼上面的轉碼可能就是畫蛇添足了。

綜上所述,我們就需要為babel提供一個判斷目標環境是否需要轉碼的方法babel_get_rule_as_need( rule_set , env_info),將經過第一次篩選後的規則集和目標用戶的環境信息傳入方法,對規則集進行再一次的精簡,那麼我們需要再次對babel進行優化:

至此,babel便具備了針對不同的使用環境進行必要轉碼的能力,可這並不是問題的全部,ES6的新特性除了語法的更新外,還增加了很多原生方法或類型,例如Map,Set,Promise等這類新的全局對象,或是Array.from這類靜態方法等等,語法轉義並不能完成對這些特性的識別,因為無論在ES5環境還是ES6環境你都是這麼寫的,只有運行的時候,瀏覽器才會報錯,告訴你某個對象或者某個方法不存在。

比如下麵的代碼:

function addAll() {
  return Array.from(arguments).reduce((a, b) => a + b);
}

轉義後會變為:

function addAll() {
  return Array.from(arguments).reduce(function(a, b) {
    return a + b;
  });
}

然而,它依然無法隨處可用因為不是所有的 JavaScript 環境都支持 Array.from。對於這一類非語法層面的特性,我們希望在工具中能夠自動提供支持,這項工作有一個專有的稱謂,叫做【polyfill】(或稱為墊片)。

我們既可以主動提供一個polyfill列表指明需要添加的墊片插件數組,也可以採用被動的方式,在轉碼過程中遇到的這種API類型的新特性放進一個數組,通過babel_add_polyfill ( polyfill_list )為根據安裝相應的墊片,需要註意的是,polyfill相當於為瀏覽器進行功能擴展,需要優先於項目業務邏輯代碼運行,那麼babel的邏輯框架就變成了:

推演繼續。在上面的邏輯結構中,我們只是簡單地將polyfill庫添加至全局變數,而全局變數是很有可能被重寫而失效或是與其他第三方庫發生代碼衝突的。那麼如果不將polyfill添加至全局,就需要將其剝離為一個具有同等功能的獨立模塊,通過類似於lodash或是underscore那樣的方式調用,我們對邏輯結構進行再一次拆分:

至此,我們已經完成了babel工具集基本功能的*邏輯層劃分*,通過傳說中的多退少補(也就是語法超前了就回退,方法不夠了就打補丁)的方式來實現代碼編譯。

三. 模塊劃分

根據上述業務邏輯層的劃分結果,我們需要對Babel工具進行代碼層的模塊劃分:

babel-module

四. 真正的babel

如果你能夠理解上述的需求推演和模塊劃分的章節,那麼恭喜你已經掌握了babel的基本結構,我們將原本模塊圖中的信息更換成實際的名稱或是插件,併進行一些組件劃分,就可以看到真正的babel工具集的基本架構:

當然真正的babel功能遠不止這樣,它為各種環境,編輯器和自動化工具提供了介面,也開放了插件開發的API給開發者,感興趣的讀者可以繼續深入瞭解。

五. 使用babel

babel8.0以上的版本將許多插件移入官方倉庫,安裝方式發生了改變,例如babel-preset-env地址變為了@babel/preset-env,使用時請參考babel官網進行配置。

1.babel-cli

為了方便直接在命令行使用babel的功能,通過yarn global add babel-cli在全局安裝命令行工具babel-cli,在package.json中加入如下腳本:

"scripts":{
    "babel":"babel main.js -o maines5.js"
}

然後通過yarn run babel即可在命令行使用babel進行編譯了,但查看編譯後的代碼就可以發現,編譯前後的文件是一樣的,因為我們沒有為其指定任何轉碼規則,運行babel只是把生成的AST遍歷了一下而已,想要babel能夠實現轉碼,請繼續向下看。

2.babel-preset-env

提供轉碼規則,它低版本babel中使用的幾個插件的結合。babel-preset-env實際上實現的,就是我們在問題推演中所描述的【All Rules規則集 + get_rules()方法集】,你會在node_modules文件夾中找到許多babel-plugin-transform-***這種命名的包,他們就是規則集,你既可以通過設置preset屬性來使用,也可以通過在plugins屬性中挑選需要的轉碼規則進行引用。

安裝babel-preset-env後在項目文件夾新建.babelrc文件並添加如下配置:

{
    "presets":["env"],
    "plugins": []
}

或自定義所需要支持的轉義規則:

{
    "presets":[],
    "plugins": [
        "babel-plugin-transform-es2015-arrow-functions"//箭頭函數轉換規則
    ]
}

再次運行babel,就可以看到所編寫的代碼已經進行了轉換。

轉換前:

//Arrow Function  Array.from method
Array.from([1, 2, 3]).map((i) => {
    return i * i;
});

轉換後:

"use strict";
//Arrow Function  Array.from method
Array.from([1, 2, 3]).map(function (i) {
    return i * i;
});

當然也可以指定目標瀏覽器,去除不必要的轉碼,例如在.babelrc指定要匹配的瀏覽器為較高版本的chrome:

//.babelrc
{
    "presets":[ 
        ["env", {
          "targets": {
             "browsers": "chrome 56"
          }      
        }]
    ],
    "plugins":[]
}

就可以發現編譯後的腳本文件中箭頭函數依然存在,說明這個版本的chrome瀏覽器已經支持箭頭函數了,也就沒有必要進行轉義了。

新版本的babel已經計劃支持在package.json中設置browserslist參數來指定需要適配的使用環境,也就是說同一套針對使用環境的配置被剝離出來,而被postcss,babel,autoprefixer等工具共用使用。

3.babel-polyfill

babel只負責語法轉換,比如將ES6的語法轉換成ES5。但如果有些對象、方法,瀏覽器本身不支持,比如:

  1. 全局對象:Promise、WeakMap 等。
  2. 全局靜態函數:Array.from、Object.assign 等。
  3. 實例方法:比如 Array.prototype.includes 等。

此時,需要引入babel-polyfill來模擬實現這些對象、方法。

如果上面編譯後的代碼在IE10瀏覽器中打開,就會看到瀏覽器出現不支持Array.from方法的報錯,如果生成的代碼需要在IE10中運行,那我們就需要引入相容補丁庫,讓IE10瀏覽器環境中能夠支持這個方法。

babel-polyfill需要通過如下的方式引入,然後通過打包工具將其融入腳本:

//ES Module
import 'babel-polyfill'
//或 CommonJs
require ('babel-polyfill')

當你真的這樣去使用時,就會發現,它的確能夠解決報錯的問題,但是如此打包會引入整個babel-polyfill,打包後的代碼增加了將近4000行(約400k體積增量),著實讓人難以接受。那這個插件能否像babel-preset-env一樣按需引用呢?必須可以的。babel-polyfill是基於core-jsregenerator構建的,只需要在引用時指明即可,例如:

import 'core-js/modules/es6.array.from';
//Arrow Function  Array.from method
Array.from([1, 2, 3]).map((i) => {
    return i * i;
});

再進行打包時就會發現bundle文件的體積減小了非常多。

babel-polyfill的實現方式如問題推演中所提到的那樣,就是污染了全局環境,而且你可能已經意識到,這個工具,要麼簡單配置後代碼量激增,要麼按需引用配置繁瑣。除非是在中型以上項目中有相容低版本IE的需求,否則不建議使用。

4.babel-runtime/babel-plugin-transform-runtime

如果一個東西難用,那麼很快就會有替代品出現,軟體的世界也是這樣,babel-runtime就是這樣一個替代品。摘錄下文資料推薦的博文中的解釋:

  • babel-polyfill

    簡單粗暴,他會污染全局環境,比如在不支持Promise的瀏覽器會polyfill一個全局的Promise對象供調用;另外,不支持的實例方法也在對應的構造函數原型鏈上添加要polyfill的方法。

  • babel-runtime

    不會污染全局環境,會在局部進行polyfill,另外不會轉換一些實例方法,如'abc'.includes('a'),其中的includes方法就不會翻譯。它一般結合babel-plugin-transform-runtime來使用。

簡單地說,除了實例方法以外,其他的特性babel-runtime都會幫你打好補丁。使用時直接在plugins配置項中添加babel-plugin-transform-runtime即可。

總的來說,babel-polyfillbabel-plugin-transform-runtime都有各自的使用場景,也是可以結合使用的,需要根據實際項目需求進行篩選和引入

六. 資料推薦

  • 《webpack+babel項目在IE下報Promise未定義錯誤引出的思考》

    博文里詳細解說了babel-runtimebabel-plugin-transform-runtime的相關問題。

  • 《如何寫好.babelrc?》

    博文里詳細解說了各個配置項和可選參數的意思,非常實用。

  • 入門指南:babel-handbook

    非常棒的入門指南,對babel中的概念和用法都做了一定解釋,建議優先閱讀,可以幫助開發者瞭解本篇中未涉及的babel模塊。

  • 官方網站:www.babeljs.io

    很多開發者喜歡看教程卻容易忽略官網,這是非常奇怪的。官方網站會鏈接到非常多優秀的github倉庫,不僅包括babel中封裝的底層模塊,還包括能夠幫助我們理解的指引倉庫,甚至ES2015主要特性的解釋的網站,是學習babel的主要資源。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一.概述 當資料庫發生損壞,資料庫的每個文件都能打開,只是其中的一些頁面壞了,這種情況可以藉助DBCC CHECKDB進行資料庫檢查修複。如果要保證資料庫不丟失,或修複不好,管理員只能做資料庫完整恢復,為了少數頁面恢復整個資料庫,代價是比較高的,sql server引入了頁面還原功能,可以指定還原若 ...
  • 今天博客的內容比較簡單,就是看一下蝦米音樂首頁中頻道選擇的一個動畫效果的實現。之前用mask寫過另外一種Tab切換的一種效果,網易雲音樂裡邊的一種Tab切換效果,詳情請移步於"視錯覺:從一個看似簡單的自定義控制項說起"。,下麵會對效果進行分析,並且根據自己的理解去實現一個類似的頻道選擇切換效果。代碼會 ...
  • 在Xcode裡面,預設為ARC(auto reference counting),也就是自動記憶體管理機制,在這裡我們要瞭解的是記憶體管理,肯定是不能讓系統幫我們管理記憶體,我們需要將ARC關閉,首先在左邊選中你所創建的工程,點擊build setting,Objective-C atomic refer ...
  • 在使用recycleView的時候出現了錯誤Scrapped or attached views may not be recycled 原因: view沒有被recycled,recyclerView的數據進行清空操作之後,在重新添加數據之前忘記了通知界面進行重新繪製,所以崩潰。 解決方法只要在c ...
  • 在開發中有時會遇見設計圖裡按鈕設計的特別小,這時會用到手動擴大UIButton的響應範圍,下麵有兩個解決辦法: 第一種方法:創建一個類目:UIButton+EnlargeTouchArea .h文件 .m文件 使用方法: 第二種:直接創建一個UIButton類,然後覆寫pointInside方法 使 ...
  • 想要把APP上架到應用市場都要先註冊開發者賬號才可以。這裡的方法包括註冊帳號和後期上架及一些需要註意的問題。註意:首次提交應用絕對不能隨便刪除,否則後面再提交會顯示應用APP衝突,會要求走應用認領流程,那個時候就會相當麻煩啦。 1、騰訊應用寶 騰訊開放平臺地址:http://open.qq.com ...
  • 基本模板 acitivity_main.xml tab_item_view.xml main_bottom_image_selector圖片選擇器 註:模板有5個tab,需要5個圖片選擇器,還需要5個文本選擇器,還需要創建5個fragment。 ...
  • 簡單學習瞭解angularjs中的指令,理解其基本概念、使用規則、簡單的自定義指令 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...