webpack – babel 篇

不吃早餐發表於2019-03-03

前言

基於我的上篇文章,webpack4.0 入門篇 – 構建前端開發的基本環境,補充對babel進行的一次探究。上篇文章講敘到的 webpack babel 時幾乎一筆帶過,所以這篇文章將進行補充說明.

Babel 是一個讓我們能夠使用 ES 新特性的 JS 編譯工具,我們可以在 webpack 中配置 Babel,以便使用 ES6、ES7 標準來編寫 JS 程式碼。

本文以當前最新版本的 babel – 7.10 為例, 做 babel 的配置. 相關版本號如下

{
  "devDependencies": {
    "@babel/core": "^7.1.6",
    "@babel/plugin-proposal-decorators": "^7.1.6",
    "@babel/plugin-transform-runtime": "^7.1.0",
    "@babel/preset-env": "^7.1.6",
    "@babel/runtime": "^7.1.5",
    "babel-loader": "^8.0.4",
    "webpack": "^4.26.1",
    "webpack-cli": "^3.1.2"
  }
}
複製程式碼

babel-loader 和 @babel/core

建立基本的 webpack 配置檔案

mkdir webpack-babel => cd  webpack-babel => yarn init -y  // 初始化
npm i yarn -g // 安裝了yarn可以忽略
yarn add webpack webpack-cli -D

// package.json 中新增:
"scripts": {
  "start": "webpack --mode development",
  "build": "webpack --mode production"
}

yarn add babel-loader @babel/core -D
複製程式碼
  • yarn : 和 npm 幾乎一樣,本文使用 yarn 安裝…
  • babel-loader: 轉義 js 檔案程式碼的 loader
  • @babel/core:babel 核心庫

根目錄下新增 webpack.config.js

const path = require(`path`)

module.exports = {
  entry: `./src/index.js`,
  output: {
    path: path.resolve(__dirname, `dist`),
    filename: `[name].[hash:8].js`
  },
  module: {
    rules: [
      {
        test: /.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: { loader: `babel-loader` } // options 在 .babelrc 定義
      }
    ]
  }
}
複製程式碼

src/index.js

const func = () => {
  console.log(`hello webpack`)
}
func()

class User {
  constructor() {
    console.log(`new User`)
  }
}

const user = new User()
複製程式碼

執行 yarn build 後就可以打包成功,打包後的程式碼是壓縮後的。而 yarn start 後的程式碼是未壓縮的。為了使程式碼可讀性高一點,我們可以在webpack.config.js新增:

module.exports = {
  //...
  devtool: true
}
複製程式碼

@babel-preset-env

打包後我們可以發現箭頭函式並未轉化為 ES5 語法!

查閱 babel plugins 文件,如果要轉義箭頭函式,需要使用到 @babel/plugin-transform-arrow-functions 這個外掛
同理轉義 class 需要使用 @babel/plugin-transform-classes

yarn add @babel/plugin-transform-arrow-functions @babel/plugin-transform-classes -D
複製程式碼

根目錄下建立 .babelrc 檔案:

{
  "plugins": [
    "@babel/plugin-transform-arrow-functions",
    "@babel/plugin-transform-classes"
  ]
}
複製程式碼

yarn build 之後可以看出 箭頭函式和類都被轉義了。

但是假如你再使用 async await 之類的 es6 語法,你還得一個個新增,這是不實際的。

@babel-preset-env 就整合了這些語法轉義外掛:

Using plugins:
transform-template-literals {}
transform-literals {}
transform-function-name {}
transform-arrow-functions {}
transform-block-scoped-functions {}
transform-classes {}
transform-object-super {}
//...
複製程式碼

使用如下:

yarn add @babel-preset-env -D
複製程式碼

.babelrc

{
  "presets": ["@babel/preset-env"]
}
複製程式碼

@babel/polyfill

Babel 預設只轉換新的 JavaScript 句法(syntax),而不轉換新的 API ,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全域性物件,以及一些定義在全域性物件上的方法(比如 Object.assign)都不會轉碼。

這樣就導致了一些新的 API 老版瀏覽器不相容。如上述所說,對於新的 API,你可能需要引入 @babel-polyfill 來進行相容

yarn add @babel-polyfill -D
複製程式碼

修改 weboack.config.js

module.exports = {
  entry: [`@babel-polyfill`, `./src/index.js`]
}
複製程式碼

yarn build 發現檔案體積大了很多,因為上面的程式碼表示將 @babel-polyfill 的程式碼也打包進去了。

當然這不是我們希望的,如何按需編譯呢? 我們可以這麼做:

index.js

import `@babel/polyfill` // 引入

const func = () => {
  console.log(`hello webpack`)
}
func()

class User {
  constructor() {
    console.log(`new User`)
  }
}

const user = new User()

new Promise(resolve => console.log(`promise`))

Array.from(`foo`)
複製程式碼

還原 webpack.config.js

module.exports = {
  entry: `./src/index.js`
}
複製程式碼

修改 .babelrc

{
  "presets": [["@babel/preset-env", { "useBuiltIns": "usage" }]]
}
複製程式碼

yarn build 後發現我們的程式碼體積就變得很小了!

@babel/runtime 和 @babel/plugin-transform-runtime

  • babel-polyfill 會汙染全域性作用域, 如引入 Array.prototype.includes 修改了 Array 的原型,除此外還有 String…
  • babel-polyfill 引入新的物件: PromiseWeakMap

這也不是我們希望出現的。

  • @babel/runtime 的作用:
    • 提取輔助函式。ES6 轉碼時,babel 會需要一些輔助函式,例如 _extend。babel 預設會將這些輔助函式內聯到每一個 js 檔案裡, babel 提供了 transform-runtime 來將這些輔助函式“搬”到一個單獨的模組 babel-runtime 中,這樣做能減小專案檔案的大小。
    • 提供 polyfill:不會汙染全域性作用域,但是不支援例項方法如 Array.includes
  • @transform-runtime 的作用:
    • babel-runtime 更像是分散的 polyfill 模組,需要在各自的模組裡單獨引入,藉助 transform-runtime 外掛來自動化處理這一切,也就是說你不要在檔案開頭 import 相關的 polyfill,你只需使用,transform-runtime 會幫你引入。
yarn add  @babel/runtime-corejs2
yarn add @babel/plugin-transform-runtime -D
複製程式碼

修改 .babelrc

{
  "presets": ["@babel/preset-env"],
  "plugins": [["@babel/plugin-transform-runtime", { "corejs": 2 }]]
}
複製程式碼

index.js 移除 import `@babel/polyfill`

@babel/plugin-proposal-decorators

新增裝飾器模式的支援

yarn add @babel/plugin-proposal-decorators -D
複製程式碼

index.js

function annotation(target) {
  target.annotated = true
}

@annotation
class User {
  constructor() {
    console.log(`new User`)
  }
}
//...
複製程式碼

.babelrc

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }],
    ["@babel/plugin-transform-runtime", { "corejs": 2 }]
  ]
}

複製程式碼

結尾

相關文章