來會會babel這個重要且神奇的工具

一顆冰淇淋發表於2022-11-24

babel 在前端工程化開發中發揮著至關重要的作用,它能將較高階的語法轉成瀏覽器可識別的程式碼,無論中 es6 中 const 、promise 還是 React、TypeScript。

以下babel線上工具中左側輸入程式碼,右側為編譯後的結果,設定需要瀏覽器相容的版本後,可以看到 "const" 編譯成 "var","箭頭函式" 編譯成 "普通函式"。

命令列使用

babel 和之前說到的 postcss 一樣,可以透過命令列執行,解析資料夾或者檔案。安裝命令列工具所需要的依賴,npm install @babel/cli @babel/core -D

透過 npx 執行 .bin 目錄下的指令,--out-file 表示輸出檔案地址,執行編譯語句 npx babel src --out-file dist/main.js

可我們發現,目標檔案和原始檔程式碼是一樣的,const 和 箭頭函式都沒有被編譯。這是因為,我們沒有設定編譯程式碼所需要的外掛。

上面程式碼中有識別符號和箭頭函式,需要【安裝】並使用這些外掛

  • 轉換箭頭函式:@babel/plugin-transform-arrow-functions
  • 轉換const:@babel/plugin-transform-block-scoping

外掛需要透過 --plugins 新增在命令後面

npx babel src --out-file dist/function.js --plugins=@babel/plugin-transform-arrow-functions
npx babel src --out-file dist/const.js --plugins=@babel/plugin-transform-block-scoping
npx babel src --out-file dist/plugins.js --plugins=@babel/plugin-transform-arrow-functions, @babel/plugin-transform-block-scoping

指定外掛之後,babel 就會根據外掛的功能再進行編譯

但外掛只能實現單一功能,我們程式碼裡一般也不只有 const 、箭頭函式這種語法,當需要處理的程式碼種類變多,可能就要羅列出非常多的外掛來執行,所以 babel 提供了一種外掛的集合,叫做"預設","預設"會根據我們所配置的需要相容處理的邏輯來進行編譯。

安裝 @babel/preset-env 依賴後,在命令列使用 --presets 來替代 --plugins。npx babel src --out-file dist/presets.js --presets=@babel/preset-env

babel-loader

除了在命令列中直接透過 babel 編譯檔案,更多情況下會在專案中對批次檔案進行處理,這時候就需要用到 babel-loader 來進行處理啦。

安裝依賴 npm install babel-loader @babel/core @babel/core是babel的核心

在webpack.config.js檔案中配置對於js的處理規則,如果對配置不清楚可以參考 webpack初體驗loader配置方式,這裡和在命令列中執行一樣,需要配置"外掛"或者"預設"。

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "./bundle.js",
    path: path.resolve(__dirname, "./dist"),
  },
  mode: "development",
  devtool: "source-map",
  module: {
    rules: [
      {
        test: /\.js/,
        exclude: /node_modules/,
        use: [
          {
            loader: "babel-loader",
            options: {
              // "外掛"是這樣配置,這裡直接使用"預設"
              // plugins: [
              //   "@babel/plugin-transform-arrow-functions",
              //   "@babel/plugin-transform-block-scoping",
              // ],
              presets: ["@babel/preset-env"],
            },
          },
        ],
      },
    ],
  },
};

這樣 babel 就會把程式碼編譯成我們所需要的相容要求

這裡我們沒有設定到底需要對什麼樣的瀏覽器進行相容,使用的就是browserslist預設配置,browserslist作用及詳細介紹

有兩種方式可以設定babel相容性處理的規則

  • browserslist:設定的相容規則對css和js都通用,可配置在 .browserslist 或 package.json 檔案中
  • targets:webpack.config.js 中配置在 loader 的options裡

以上兩種方式都可以配置,但更建議使用 browserslist,因為 css 和 js 需要相容的瀏覽器版本一般來說是一致的,使用 browserslist 只需要配置一套規則即可。

比如我們設定 相容谷歌瀏覽器版本99

// package.json
"browserslist": [
    "chrome 99"
]

// 配置在 webpack.config.js 中
use: [
  {
    loader: "babel-loader",
    options: {
      presets: [
        [
          "@babel/preset-env",
          {
            targets: ["chrome 99"],
          },
        ],
      ],
    },
  },
],

因為 chrome 99 是最新的瀏覽器版本,對於高階的js語法都能夠支援,所以不需要將es6編譯成es5的語法。

babel 處理檔案不止在 js 中需要,如果是 react 專案的話,還需要支援 jsx 檔案、可能還存在 ts 檔案,所以可以將 babel 配置資訊放到一個單獨的檔案中,達到共享的目的。

// webpack.config.js 
rules: [
    {
      test: /\.js/,
      exclude: /node_modules/,
      use: [
        {
          loader: "babel-loader",
        },
      ],
    },
],

// babel.config.js
module.exports = {
  presets: ["@babel/preset-env"],
};

polyfill

以上 babel 可以對於一些 js 語法進行處理,但有些新特性比如 promise 在預設情況下不會被處理,這樣不能被低版本瀏覽器所識別。

那麼我們需要藉助"polyfill"來打補丁,對某些特定的語法做處理,這樣編譯後的程式碼中就能包含這些新特性。

  • babel 7.4 以前,透過 @babel/polyfill
  • babel 7.4 以後,透過 core-js regenerator-runtime

這裡使用的 babel 版本是7.17,所以使用第二種方式,安裝依賴 npm install core-js regenerator-runtime @babel/runtime-corejs3,在 babel.config.js 中給 preset 配置 polyfill 相關的屬性

  • useBuiltIns:規定以什麼方式來使用 polyfill

    • false:不使用 polyfill,不用配置 corejs
    • usage:自動檢測原始碼所需要使用的 polyfill,需配置corejs
    • entry:匯入所有 polyfill,檔案體積很大,需配置corejs,以及在入口檔案中引入 core-js 與 regenerator-runtime
  • corejs:所使用的 corejs 版本
// babel.config.js
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        useBuiltIns: "entry",
        corejs: 3,
      },
    ],
  ],
};

// 入口檔案
import "core-js/stable";
import "regenerator-runtime/runtime";

使用所有的 polyfill ,編譯後的程式碼行數達到了一萬七,裡面包括很多新特性的實現

對React的支援

react 使用的是 jsx 語法,babel 可以直接對其處理,與 js 檔案的處理方式一樣,可以選擇使用"外掛"或者外掛的集合"預設"。

外掛

  • @babel/plugin-syntax-jsx
  • @babel/plugin-transform-react-jsx
  • @babel/plugin-transform-react-display-name

預設

  • @babel/preset-react
// babel.config.js
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        useBuiltIns: "entry",
        corejs: 3,
      },
    ],
    ["@babel/preset-react"],
  ],
};

react 程式碼被處理且編譯成功,可以在瀏覽器上看到渲染的頁面效果

處理TypeScript檔案

在專案中處理 ts 檔案有兩種方式

  • ts-loader:將ts檔案編譯成js檔案,有型別檢測,但沒有polyfill
  • babel-loader:有polyfill,沒有型別檢測,程式碼錯誤不會提示

首先來看 ts-loader 處理的情況,安裝 ts-loader,ts-loader 依賴於 typescript 這個工具,npm install ts-loader typscript -D

在 webpack.config.js 配置 ts-loader 處理 ts 檔案

module: {
  rules: [
    {
      test: /\.ts$/,
      exclude: /node_modules/,
      use: [
        {
          loader: "ts-loader",
        },
      ],
    },
  ],
},

可以看到,ts 程式碼已經被完全編譯成了 js 程式碼,但是 Promise 是沒有被處理的

換成 babel-loader 來處理,和 react 、js 一樣,可以選擇"外掛"或者外掛的集合"預設"。

  • 外掛 @babel/plugin-transform-typescript
  • 預設 @babel/preset-typescript

將webpack.config.js 中的 ts-loader 替換成 babel-loader,以及在 babel.config.js 中進行配置。

// babel.config.js
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
    ["@babel/preset-typescript"],
  ],
};

這種情況下,編譯後檔案就增加了 polyfill 的處理,這裡 useBuiltIns 使用的是 usage,比 entry 這種模式下程式碼量要少一些。

從 polyfill 的角度來說,babel-loader 處理 ts 檔案會更好,畢竟我們寫的程式碼可能會有新特性和相容性要求,但 babel-loader 存在的問題是無法進行錯誤型別檢測,也就是檢測不到程式碼出錯,仍然會編譯透過。

但我們是希望能夠在開發階段儘早的發現錯誤,這樣就能避免測試階段或者釋出線上存在問題,在這裡可以配合編譯的命令來解決這個問題。

tsc 就是 typescript 工具的命令列語法,執行 npm run build透過 tsc 來檢測程式碼是否正確,正確時才使用 webpack 編譯,或者執行 npm run type-check-watch實時檢測ts程式碼是否正確

// package.json
"scripts": {
    "build": "npm run type-check && webpack",
    "type-check": "npx tsc --noEmit",
    "type-check-watch": "npx tsc --noEmit --watch"
},

當程式碼中存在錯誤語法時,會直接報錯。

這樣就可以對ts檔案達到型別錯誤的檢測以及polyfill的效果了。

以上關於 babel-loader 處理 js、react、ts 檔案,以及對於新特性的 polyfill,設定瀏覽器相容版本,babel 配置檔案 都非常的重要, 更多有關webpack的內容可以參考我其它的博文,持續更新中~

相關文章