再學babel配置

babel 每次學習都有新的理解,哪怕是其配置都與咱們前端生態中的各類概念息息相關。近期再次複習babel知識從而更好的編寫js類庫,本文是學習過程所作的記錄。前端

babel 生態裏的一些npm模塊

  • babel/core 核心轉義功能
  • babel/cli 命令行工具,能夠經過babel命令來轉換代碼,或者轉換文件,輸出轉義後的文件。如 babel src --out-dir lib --watch
  • babel/node 是用他來執行es6+代碼,以node方式執行。適合於去執行你寫的nodejs代碼。

那麼,咱們使用 webpack 來打包js代碼時,是使用的什麼呢?答案是 babel-loader。node

babel-lodaer 是適配 webpack 的一個模塊,其內部就是調起 babel/core 來實現代碼轉譯。webpack

理解轉譯其實有2種類型的轉譯

記住:babel自身惟一能作的事情叫:代碼轉譯。
至於新增api那些,那是須要引入polyfill墊片的事情(下文會講)。es6

babel自己所作的事情,通常都叫他代碼轉譯。那麼所謂的轉譯都有哪些種類呢?以下:web

一、單純的語法解析和轉換。例如箭頭函數轉成function
二、須要一點輔助的語法解析和轉換,例如 class語法,給你轉成prototype原型語法chrome

解釋下第二點:咱們知道,一種新的語法意味着新的一些關鍵字和符號等,那麼對於一些特殊符號如箭頭函數,那麼他剛好有es5對應的 function 關鍵字,因此babel只需簡單換一下字符。npm

然而有些卻不是簡單的換一下,例如 class 語法,babel給你轉成 es5 以後,他必須改形成函數和prototype的寫法,在這種狀況下,babel編譯後的代碼中會加入一些輔助函數(也就是下文說的babel/runtime所幹的事兒)以協助來用es5的方式實現 class。api

舉個栗子:
image.pngpromise

看到了嗎,babel生成的原型式寫法中,須要在構造函數中對調用者的實例化方式進行檢測,這些都封裝成了 helper函數 _classCallCheck。瀏覽器

除此以外,還有更復雜的場景 babel轉譯以後甚至須要依賴polyfill墊片:例如 async 轉換。 babel 會把 async 語法代碼轉成 generator 輔助函數,而 generator 輔助函數的功能須要依賴 regenerator-runtime 這個polyfill墊片。以下圖,babel給你把async語法轉換後,多了不少輔助函數;甚至其中有一個 regeneratorRuntime 的函數是沒有找到定義的,而這個函數其實就是依賴全局須要引入 regenerator-runtime 這個 polyfill 才行。

image.png

理解什麼是 corejs 和 babel/runtime

首先,咱們要理解什麼是 polyfill 什麼是輔助函數。

像上文所說的2種babel轉譯,其中轉換以後所出現的那些輔助函數,叫作運行時所須要的helper,他們其實就是 runtime。 而這些運行時函數,有一個單獨的 npm 包去實現他們,叫作 babel/runtime。

而像那些 es6 新增的 API,例如 Promise、Map、WeakMap。數據新方法 flat、includes 等等 api,這些東西不屬於 babel語法編譯的範疇,是屬於一些新的api。這些叫作 墊片,英文名叫 polyfill。在社區裏,polyfill墊片經過另外2個npm包來實現:一個叫作 corejs(用來實現除了generator以外的墊片,如今要使用他的版本3),另外一個叫作 regenerator-runtime(用來實現generator墊片)。

實戰:配置 babel 的最佳實踐

其實最佳實踐的前提就是正確的理解上文中兩個概念。從而正確的使用 babel/runtime 和 corejs3.

若是比喻成作飯,babel/runtime 和 corejs3 就是咱們的食材。有了食材,咱們就要用一個配套的刀具來加工他們。

  • babel/runtime配套的刀具是:babel/plugin-transform-runtime
  • corejs3的配套刀具是 preset/env 預設。

咱們接下來所講的實踐,其背景是在 webpack + babel 下的實踐。由於,畢竟咱們一般不會單據來用 babel 操做一個 js 文件。

webpack 打包一個網站應用js時

這種場景的要求是:

  • 按需轉譯語法和輔助函數。例如若是個人目標平臺支持了箭頭函數,那麼babel請不要給我轉換成es5那種語法。
  • 全局polyfill墊片便可,不怕污染,由於我指望面向我產品全部用戶使用的瀏覽器
  • 儘量的少打包墊片。即按需polyfill。由於咱們指望儘量減小沒必要要的體積。好比我面向的用戶瀏覽器大於IE10,那麼IE9的polyfill不要打;同時,若是我代碼中沒有使用promise,那麼promise的墊片也不要給我打
  • helper那些輔助函數,不要每一個js模塊中都寫一份(由於babel自己確定是針對每一個js編譯的,因此默認每一個js確定都會出現輔助函數)

解決方法:

一、 爲了能按需轉譯語法。在最新babel7以後,咱們只需使用preset/env預設就很簡單了。

// babel.config.js
module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": false, // polyfill配置先關掉,後面再講
      }
    ]
  ]
}

咱們能夠經過修改 targets ,來決定babel編譯哪些語法。例如你把chrome版本號調到最新1個版本,那麼箭頭函數必然是不會轉換的。

二、 三、按需加載polyfill

按需加載polyfill有2種方式,一種是把 useBuiltIns配置成 entry,另外一種是配置成 usage.

使用上的區別是:

  • entry:須要你手工在你的webpack 入口js裏,引入一下corejs和regenerator-runtime這倆polyfill。babel編譯後,會自動在入口js裏,把你那2行換成面向目標targets按需引用的corejs模塊。
  • usage:不須要你手工引入。babel會自動把 corejs庫的模塊放到你的每一個js模塊裏。放置的原則就是:不只面向目標targets來按需引入,並且還按照你代碼中是否使用來引入。假如你的a.js裏用了promise,那麼他會把corejs中promise模塊引入。

以下是usage的方式:

module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": 'usage',
        "corejs": 3 // 寫死就好!當前階段就該用3
      }
    ]
  ]
}

以下是entry方式

//babel.config.js
module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": 'entry',
        "corejs": 3
      }
    ]
  ]
}

entry模式時,須要在代碼裏手工引入一下。

// index.js
// 手工引入
import "core-js/stable";
import "regenerator-runtime/runtime";

既然usage這麼好,咱們何苦要用entry呢? 因此,就用usage模式吧!

四、問題來了:怎麼解決輔助函數的冗餘問題。

babel/runtime是幹啥的,要雜用?

上文的配置,咱們解決了polyfill的問題。下一步,咱們要解決輔助函數在每一個js裏都有冗餘的問題。由於如今的配置下,若是你有 a.js 和 b.js,那麼兩個js中都會被babel放入那些 helper 輔助函數。當webpack打包完成一個 bundle。學過webpack原理的應該指到:最終的bundle裏也會有 a.js 和 b.js模塊,每一個模塊中必然也存在重複的輔助函數。

怎麼解決? 那就用 babel/plugin-transform-runtime 插件。
這個插件的工做就是:在babel編譯每一個js時,把裏面的輔助函數給換成 對 babel/runtime 的 require 引用。

當每一個js裏的輔助函數,都變成 babel/runtime的引用。那麼webpack打包後的bundle,必然這些輔助函數就變成一個公共模塊了,解決了冗餘問題。

配置方式:

module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": 'usage',
        "corejs": 3
      }
    ]
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "helpers": true,  // 這就是抽離helper 的配置
      "corejs": false, // 先設置false,後面再講
      "regenerator": false // 先設置false,後面再講
    }]
  ]
}

上面把 helper配置爲true,就能夠實現helper函數抽到 babel/runtime。

至此,網站打包講解配置完成!開發網站時就用上述配置便可。

webpack 打包一個 jssdk 時

開發jssdk跟開發網站對bundle.js有不一樣的要求。

  • 咱們指望咱們的sdk能夠支持不少target環境,所以須要polyfill墊片。但咱們不知道jssdk的運行環境是否有墊片。這裏有2個方法,方法1:經過文檔告訴開發者你須要加哪些墊片;方法2:咱們本身polyfill,可是不能污染全局
  • jssdk若是本身進行polyfill。那固然也但願按需polyfill,減小體積
  • 輔助函數一樣須要減小冗餘,跟上文網站相同。

能夠顯而易見,對於開發jssdk,咱們第一要作的,就是先把全局polyfill給他關掉。好比關掉那個 usage:

module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": false
      }
    ]
  ]
}

幸運的是,jssdk的另外幾個要求,靠 babel/plugin-transform-runtime 均可以搞定。以下配置便可:

module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": false
      }
    ]
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "helpers": true,  // 這就是抽離helper 的配置
      "corejs": 3, // 這是局部polyfill的配置
      "regenerator": true // 這是局部polyfill generator的配置
    }]
  ]
}

咱們只需把該插件的配置全開。把 preset/env 的polyfill配置關掉,就是一個適配jssdk開發的配置了。

相關文章
相關標籤/搜索