# [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` 設定

(若單純寫 `./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'
},
```

(可以發現我的 src 前面有 https://google.com,但因為這個路徑不存在,所以圖片掛掉了)
## Asset Modules

### 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,讓瀏覽器可以讀取

(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/)