webpack v3 學習筆記(一) 打包js

WanFengZ發表於2019-12-21

webpack

隨著前端技術的發展,前後端開發的分離,前端專案的構建顯得越來越有必要,而 Webpack 就是一個前端的打包工具,幫助我們完成前端專案的構建。

前端的專案構建工具也逐漸的由藉助其他語言的構建工具,到使用 Grunt,後來被 Stream 概念的 Gulp 所取代,再到現在的 Rullup, Webpack。而 Webpack 也成為了三大框架官方腳手架所使用的構建工具,在我們做專案的時候會經常接觸到,非常有學習瞭解的必要。

其起源於作者在 Google 就職時,後專案被 Instagram 支援。

其各個版本更迭情況大概如下:

  1. v1
    • 編譯,打包
    • 模組熱更新
    • 程式碼分割
    • 檔案處理
  2. v2
    • Tree Sharking
    • 支援 ES Module
    • 動態 Import()
  3. v3
    • Scope Hoisting
    • Magic Comments
  4. v4
    • 零配置
    • mode
    • 新增支援的 js 模組
    • 外掛的改變和優化

Webpack 的作用,用官網的一張圖就能概括:

webpack v3 學習筆記(一) 打包js

核心概念

v3 中有四個核心概念:entry,output,loader,plugin

entry

entry 是 webpack 進行打包的入口,webpack 會從入口開始去尋找入口檔案的依賴項進行打包。

output

output 是 webpack 打包後的輸出檔案。

loader

webpack 本身只能打包 js,而其他型別的檔案就要藉助於各種各樣的 loader。

plugin

外掛可以幫助我們實現更多樣的功能。

打包普通 js

npm init
npm install webpack@^3.0.0
touch webpack.config.js
mkdir src
touch src/app.js
touch src/sum.js
複製程式碼

app.js:

import sum from './sum'

console.log(sum(1 + 2))
複製程式碼

sum.js:

export default function (a, b) {
  return a + b
}
複製程式碼

webpack.config.js

module.exports = {
  entry: {
    app: './src/app.js'
  },
  output: {
   // output 中可以使用佔位符
    filename: '[name].[hash].js',
    path: __dirname + '/dist'
  }
}
複製程式碼

執行 webpack 打包命令即可成功打包。

打包 ES6

npm init
npm install webpack
// babel包會在下面解釋作用
npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env @babel/polyfill @babel/runtime-corejs3 @babel/plugin-transform-runtime core-js regenerator-runtime
touch webpack.config.js
touch .babelrc
mkdir src
touch src/app.js
touch src/sum.js
複製程式碼

app.js:

import sum from './sum'

let a = 1
const b = 3
const c = [...[1, 2], 3]

console.log(sum(a + b + c[0]))
複製程式碼

sum.js:

export default (a, b, c) => a + b + c
複製程式碼

為了讓 webpack 識別並打包 ES6 語法,我們就需要使用 babel 和 babel-loader 了。

如果要使用 babel v7 版本,則 babel-loader 需要安裝 babel-loader@8.0.0-beta.0

webpcak.config.js

module.exports = {
  entry: './src/app.js',
  output: {
    filename: '[name].[hash].js',
    path: __dirname + '/dist'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: '/node_modules/'
      }
    ]
  }
}
複製程式碼

.babelrc

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

我們在 rules 裡配置了 .js 結尾的檔案呼叫 babel-loader。使用了 @babel-core(核心) 和 配置了 @babel/preset-env (識別ES6語法),

執行 webpack 命令打包,ES6 語法正確轉譯:

webpack v3 學習筆記(一) 打包js


@babel/preset-env 只是讓babel 支援了新的 ES6 語法,但是 ES6 中新加的功能在一些低階的瀏覽器中並不會被支援,這就需要使用到我們安裝的其他的 babel 包了。下面說到的包其實都是必須要安裝到 --save 中的, 因為這些包都是要在應用中使用到的(@babel/plugin-transform-runtime 為 dev,搭配 @babel/runtime 使用)

其主要分為兩類使用:

@babel/polyfill

@babel/polyfill 其是就是一個功能包,裡面幫我們實現了 ES6 的各種 API,我們只需要在入口檔案中引入即可:

import 'babel-polyfill'
複製程式碼

注意這樣我們的打包生成檔案會大上許多,多出來的就是我們引入的全部的 polyfill 的大小,另外,引入的 polyfill 是會汙染全域性變數的。

使用core-js v2 的時候可以像上面引入。

core-js 到了 v3,@babel/polyfill 被廢棄了,改而要使用 core-js regenerator-runtime,之前的全部引入 polyfill 也要改成:

import 'core-js/stable';
import 'regenerator-runtime/runtime';
複製程式碼

在 babel 中,還可以通過 preset-env 中的配置來實現 polyfill 的按需引入來節省空間:

.babelrc:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        // 指定core-js為v3
        "corejs": 3
      }
    ]
  ]
}
複製程式碼

@babel/runtime & @babel/plugin-transform-runtime

藉助於 @babel/runtime & @babel/plugin-transform-runtime 同樣可以實現支援 ES6 API,並且這種方式是按需引入的,還不會汙染全域性變數。

之前的說法是這種方式比較適合開發庫使用,但是這種方式是有缺陷的,如 [1,2,3].includes(1) 這種依賴於 Array.prototype.includes 的方法就不支援。所以在產品中還是使用 polyfill 的方式為好。

但是在新版本中可以指定 core-js 為 v3(對應的 runtime 包為 @babel/runtime-corejs3),就支援了,所以目前來看還是 runtime 的方法最優。

使用時也只要在 .babelrc 中配置:

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

打包 typescript

打包 ts 我們要藉助 ts-loader 和 tsconfig.json(ts 編譯設定檔案)。

但是隨著babel的發展,其實是更推薦使用 babel 的方式去轉譯 ts。至於為什麼,我想把答案交給下面這篇譯文:juejin.im/post/5c822e…

ts-loader

npm init
//  最新的ts-loader要求webpackv4
npm install webpack@^4.0.0 webpack-cli typescript ts-loader
touch tsconfig.json
touch webpack.config.js
mkdir src
touch src/app.ts
複製程式碼

app.ts:

const a = 45

interface Cat {
    name: String,
    sex: String
}

function touchCat(cat: Cat): void {
    console.log('miao, I am ' + cat.name)
}

touchCat({
    name: 'kafe',
    sex: 'male'
})
複製程式碼

webpack.config.js:

module.exports = {
  mode: 'development',
  entry: './src/app.ts',
  output: {
    filename: '[name].[hash].js',
    path: __dirname + '/dist'
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader'
      }
    ]
  }
}
複製程式碼

tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "allowJs": true
  },
  "include": [
    "./src/*"
  ],
  "exclude": [
    "./node_modules"
  ]
}
複製程式碼

@babel/preset-typescript

npm init
npm install webpack@^3.0.0 babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env @babel/preset-typescript
touch webpack.config.js .babelrc
mkdir src
touch src/app.ts
複製程式碼

app.ts:

const a = 45

interface Cat {
    name: String,
    sex: String
}

function touchCat(cat: Cat): void {
    console.log('miao, I am ' + cat.name)
}

touchCat({
    name: 'kafe',
    sex: 'male'
})
複製程式碼

webpack.config.js:

module.exports = {
  entry: './src/app.ts',
  output: {
    filename: '[name].[hash].js',
    path: __dirname + '/dist'
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'babel-loader',
        exclude: '/node_modules/'
      }
    ]
  }
}
複製程式碼

.babelrc:

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

打包後如下:

webpack v3 學習筆記(一) 打包js

注意這樣因為沒有使用 tsc 編譯,所以是沒有型別檢查的,如果需要這個功能,可以使用webpack中的 ts 型別檢查外掛。

相關文章