---
# System prepended metadata

title: '[Udemy] Learn Webpack 5 from the very basics to advanced! '
tags: [Udemy 課程筆記, 前端筆記]

---

# [Udemy] Learn Webpack 5 from the very basics to advanced!

###### tags: `Udemy 課程筆記` `前端筆記`

> Webpack was designed to help you bundle all your dependencies into one or more fles.

## 預設的設定檔案

Webpack 預設就會吃 `webpack.config.js` 的設定檔案，因此若想額外設定就找到或者建立這個檔案操作就行。

## 基礎設定

Webpack 預期要一個物件，這個物件就是設定檔案。

```javascript!
const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path:  path.resolve(__dirname, './dist')
    },
    mode: 'none'
};
```

## Entry

這個屬性告訴 Wepback 從哪裡進入開始 building，預設是 `./scr/index.js`。

### 可以多個 entry points

以公司專案為例，這裡根據不同的功能區分不同的模組，在 Webpack 中也可以個別分開 bundle

```javascript!
export.module = {
  entry: {
    app: './src/main.jsx',
    business: './src/business.main.jsx',
    guest: './src/guest.main.jsx',
    verify: './src/verify.main.jsx',
  },
  // ...
}
```

## Output

> 在很多英文的教學中，output 的檔案就會被稱作 **bundle**。

這個屬性告訴 Webpack bundle 過的檔案放哪裡已經其檔名。


### `output.filename`

針對 bundle 的檔名額外設定，可以支援巢狀資料夾的寫法：

```javascript
const rootDir = path.resolve(__dirname, '../');

module.exports = {
  output: {
    path: `${rootDir}/publish`,
    // 最後會在 ../publish/js/... 產出 bundle 檔案
    filename: `js/${timestamp}/[name].[chunkhash].bundle.js`,
  },
}
```

:::info
Webpack 保留特別檔名，供開發者在檔名中可以放置 Webpack build time 時產生的變數：

- `[id]` \- chunk id (e.g. `[id].js` -> `485.js`)
- `[name]` \- chunk name (e.g. `[name].js` -> `app.js`). If a chunk has no name, then its id will be used
- `[contenthash]` \- md4-hash of the output file content (e.g. `[contenthash].js` -> `4ea6ff1de66c537eb9b2.js`)

還有很多，可以在[這裡](https://webpack.js.org/configuration/output/#template-strings)找到。
:::

### `path` 必須要放絕對路徑，可以使用 `path` 設定

![截圖 2024-06-25 23.51.44](https://hackmd.io/_uploads/HkLddw_8C.png)
（若單純寫 `./dist` 就會出現這個錯誤）

>__dirname is a Node. js-specific variable that is used to obtain the name of the directory from a given file path. 

## 讀取客製化 configuration

```jsonld!
// package.json

{
  // ...
  "scripts": {
    // 使用 --config 可以指定 Webpack 該讀取哪個路徑的設定檔案
    "build": "webpack --config webpack/webpack.config.js"
  },
  // ...
}

```

## 模組 Module

> These options determine how the [different types of modules](https://webpack.js.org/concepts/modules) within a project will be treated.

> Module 決定 Webpack 怎麼處理專案的 module。

```javascript!

// 基本使用 => 將專案的圖片（使用 `test` 的 regular expression 決定）以 Webpack 內建的 asset/resource 處理

module: {
  rules: [
    {
      /** test: 哪些模組要使用？ */
      test: /\.(png|svg|jpg|jpeg|gif)$/i,
      /** type: 使用內建的方式處理 */
      type: 'asset/resource',
    }
  ]
}

```

### publicPath

在 `output.publichPath` 可以設定部署至遠端或者靜態檔案路徑：


```javascript!
output: {
  filename: 'bundle.js',
  path: outputDir,
  /** 預設是 auto */
  publicPath: 'https://google.com'
},
```

![截圖 2024-06-30 00.05.21](https://hackmd.io/_uploads/Hkl0xn6IC.png)
（可以發現我的 src 前面有 https://google.com，但因為這個路徑不存在，所以圖片掛掉了）


## Asset Modules

![截圖 2024-06-27 00.25.35](https://hackmd.io/_uploads/H10ZZpKU0.png)

### asset/resource

Webpack 內建就有 `asset/resource` 處理圖片：

```javascript!
// 將圖片 `import`
import Kiwi from './assets/kiwi.jpeg'


const addImage = () => {
	const image = document.createElement('img')
	const body = document.querySelector('body')

	image.src = Kiwi
	image.alt = 'kiwi image'
	image.width = 150

	body.appendChild(image)
}

```

Webpack bundle 時就會自動處理這個圖片。

## Loaders

除了使用 Webpack 內建的工具外，也可以使用 loader 處理：

:::warning
使用 `use: []` 串列各種不同的 loaders，loader 執行完的結果就會給下一個 loader 當作 input 串接。要注意的是 loader 執行的順序是由右至左。
:::

```javascript!
module: {
  rules: [
    {
      test: /\.css$/,
      /** 以 style-loader 及 css-loader 額外處理專案中 css 檔案 */
      /** loader 的執行順序是由右至左，所以是先執行 css-loader => style-loader */
      use: ['style-loader', 'css-loader'],
    }
  ]
}
```

```javascript!
// ref. https://webpack.js.org/configuration/module/#ruleuse

module.exports = {
  //...
  module: {
    rules: [
      {
        //...
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
            },
          },
          {
            loader: 'less-loader',
            options: {
              noIeCompat: true,
            },
          },
        ],
      },
    ],
  },
};
```

### css-loader + style-loader = css 載入組合拳

- 先透過 css-loader 讓 webpack 讀取 javascript 中引入的 css 檔案
- style-loader 會在 `<head>` 標籤內直接注入其 style，讓瀏覽器可以讀取

![截圖 2024-06-30 23.10.32](https://hackmd.io/_uploads/Hkw_Bg1D0.png)
（Webpack 在 `<head>` 注入 `<style>`）

### 除了 css 當然還可以用 sass/scss

- `sass`: 讓專案可以使用 sass/scss
- `sass-loader`: 讓 Webpack 可以讀取 javascript 中引入的 sass/scss 檔案（`sass-loader` 還有很多設定，有需要可以去文件看 config）


```javascript!
module: {
  rules: [
    {
      test: /\.css$/,
      /** 以 style-loader, css-loader, sass-loader 額外處理專案中 scss 檔案 */
      /** loader 的執行順序是由右至左，所以是先執行 sass-loader => css-loader => style-loader */
      use: [
        'style-loader',
        'css-loader',
        /** 
         * 參考公司專案：拿 "${sourceDir}/configs/style/base.scss" 路徑的 scss 變數注入專案，這樣子專案就可以直接讀取共用變數
         **/
        {
          loader: 'sass-loader',
          options: {
            additionalData: `@use "${sourceDir}/configs/style/base.scss" as *;`,
          }
        }
      ],
    }
  ]
}
```

## Plugins

下列是課程中提到的 Plugins：

### [TerserWebpackPlugin](https://webpack.js.org/plugins/terser-webpack-plugin/)

用來讓專案的 JavaScript 變醜以便減少 bundle 大小。

### [MiniCssExtractPlugin](https://webpack.js.org/plugins/mini-css-extract-plugin/)

將 CSS 等其他 style(SASS, SCSS...) 從 JavaScript 的 bundle 抽出成獨立的 CSS 檔案，並且在 production 環境中會把需要的 CSS 獨立引入。

### [CleanWebpackPlugin](https://github.com/johnagan/clean-webpack-plugin)

執行 bundle 指令以前，將先前產出的 bundle 結果刪除。

### [HtmlWebpackPlugin](https://webpack.js.org/plugins/html-webpack-plugin/)

Webpack 自動產生引入 bundle 檔案的 HTML。

```javascript
module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
        /** template 供開發者可以選擇以哪一個 HTML 檔案為樣板 */
        template: './src/template/index.html',
        /** Webpack 自動產生 HTML 的檔名 */
        filename: 'index.html',
    })
  ],
}
```

```htmlembedded
<!-- src/template/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
</head>
<body>
</body>
</html>
```

```htmlembedded
<!-- Webpack 產出的 HTML，自動引入 bundle 完的靜態檔案 -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script defer src="bundle-1c5cb6d2081cb3824984.js"></script>
    <link href="main-17af59139acd9c1396ca.css" rel="stylesheet">
  </head>
  <body></body>
</html>
```

#### 若專案需求，同一個專案分成多個 entry 時，HtmlWebpackPlugin 也可以按照 entry 區分不同的 HTML

參考公司的專案，有分成四個 entry，所以 Webpack 會依照這四個入口掃描其所屬模組：

```javascript
entry: {
  app: './src/main.jsx', // app 的入口
  business: './src/business.main.jsx', // business 的入口
  guest: './src/guest.main.jsx', // guest 的入口
  verify: './src/verify.main.jsx', // verify 的入口
},
```

```javascript
// 每一個 HtmlWebpackPlugin 實例都是針對個別 entry 處理
plugins: [
  /** 處理 app */
  new HtmlWebpackPlugin({
    // 預設的情況下 HtmlWebpackPlugin 會將全部 entry 的 bundle 都塞入建立出來的 HTML 中，若要排除則需要透過 excludeChunks 設定
    excludeChunks: ['business', 'guest', 'verify'],
    template: './src/assets/index.html',
    // inject: 'body' 會將預設 bundle 從 <head> 放入 <body> 最底端
    inject: 'body',
    hash: true,
  }),
  /** 處理 business */
  new HtmlWebpackPlugin({
    excludeChunks: ['app', 'guest', 'verify'],
    filename: 'business.html',
    template: './src/assets/business.index.html',
    inject: 'body',
    hash: true,
  }),
  /** 處理 guest */
  new HtmlWebpackPlugin({
    excludeChunks: ['app', 'business', 'verify'],
    filename: 'guest.html',
    template: './src/assets/guest.index.html',
    inject: 'body',
    hash: true,
  }),
  /** 處理 verify */
  new HtmlWebpackPlugin({
    excludeChunks: ['app', 'business', 'guest'],
    filename: 'verify.html',
    template: './src/assets/verify.index.html',
    inject: 'body',
    hash: true,
  }),
],

```

### WebpackDevServer

實現 live reload 的套件。





## Code splitting

> 每一個被分割的模組就是 **chunk**。


## 參考資料

1. [Webpack 5: The Complete Guide For Beginners](https://www.udemy.com/course/webpack-from-beginner-to-advanced/)
2. [A mostly complete guide to webpack 5 (2020)](https://www.valentinog.com/blog/webpack/)
3. [Webpack 前端打包工具 - 使用 html-webpack-plugin 生成 HTML 文件](https://awdr74100.github.io/2020-03-23-webpack-htmlwebpackplugin/)