# [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/)