認識 webpack

前端最強打包工具。

為什麼要用 webpack?

我覺得最主要的原因有兩點:

  1. 全域變數的衝突
  2. 希望在瀏覽器上引入 npm 下載的套件

過去在瀏覽器上引入套件時,通常是:

1
2
3
4
5
<!-- jQuery -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script>
// 接著在這裡就能用 $ 或 jQuery 來使用
</script>

src 是最簡單的方法,不過現在全域空間中就有 $jQuery 這兩個變數。

問題來了,如果今天有另一個套件也用 $ 來命名怎麼辦?這個就是全域變數的命名衝突

但如果是 Node.js 的話就沒有這個問題,因為我們可以這樣做:

1
2
// 自己命名
const $ = require('jquery')

所以後來 ES6 推出了「ES Modules」,讓瀏覽器能夠有原生的「模組機制」。

但還是有一些問題:

  • 不是所有瀏覽器都能支援
  • 使用上蠻麻煩的(要加上 type=module 和搭配 server 執行才能使用)
  • 依然沒辦法用把 node_modules 裡的東西引入進來用

永遠記得一句話:

瀏覽器不支援或支援度很差的話,就自己寫一個工具來讓它支援就好了。

所以為了解決以上問題,webpack 就誕生了。

如果我想要引入用 npm 下載的 jquery,我可以開一支檔案來寫 jQuery:

1
2
3
4
5
6
7
8
9
// CommonJS
const myjQuery = require('jquery')
myjQuery('body').css('background-color', 'rgba(0, 255, 0, 0.2)')
myjQuery('.btn').on('click', () => console.log('click'))

// ES modules
import jq from 'jquery'
jq('body').css('background-color', 'rgba(0, 0, 255, 0.2)')
jq('.btn').on('click', () => console.log('click'))

不管我要在裡面用 CommonJS 或 ES modules 來引入都沒問題。我只要記得寫好後用 webpack 打包,接著在 HTML 裡面加上一句:

1
2
<!-- 打包好的模組 -->
<script src="./bundle.js"></script>

一切就完成了,而且還真的可以跑,很神奇吧?但不要忘了,這一切都是因為有 webpack 幫你在背後做處理,否則瀏覽器不可能做得到這些。

至於為什麼 webpack 可以做到?你可以想成是「webpack 直接幫你在瀏覽器上實作 requireimport 這兩個函式」,大概是這樣。

總而言之,再次強調:

  • webpack 是用來把模組打包起來的工具
  • webpack 是用來把模組打包起來的工具
  • webpack 是用來把模組打包起來的工具

補充-引入時是怎麼從 npm 裡面找到的?

其實沒有什麼黑魔法,以 jQuery 的例子來說,你可以在 node_modules 裡找到 jquery,資料夾結構大概是這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
│   AUTHORS.txt
│ bower.json
│ LICENSE.txt
│ package.json
│ README.md

├───dist
│ jquery.js
│ jquery.min.js
│ jquery.min.map
│ jquery.slim.js
│ jquery.slim.min.js
│ jquery.slim.min.map

├───external

└───src

接著打開 package.json 搜尋 main 會看到:

1
2
3
{
"main": "dist/jquery.js"
}

所以這就是被引入進來的 jQuery。其他套件也是這樣運作的,真的沒有什麼黑魔法,只是這樣而已。

webpack.config 基本結構

config 是用來設定 webpack 要怎麼打包,或是一些額外資訊,這邊先寫個最簡單的架構:

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
// 進入點,簡單來說就是引入所有 module 的那支檔案
entry: './src/main.js',
// development 或 production(預設)
mode: 'production',
output: {
// 輸出位置
path: __dirname,
// 檔案名稱
filename: 'bundle.js'
}
}

認識 loader

webpack 強大的地方在於它不只把 JavaScript 當作模組,甚至能把圖片、CSS 等資源都當成是模組

為了實現這個功能,必須要有一個東西來處理,那個東西就是「loader」。

簡單來說就是把「資源」載入成「瀏覽器看的懂的東西」,這就是 loader 的負責的工作。

這邊做個示範,假設我想要在 JavaScript 裡面載入 CSS:

1
2
3
// 兩種都可以
import css from './style.css'
const css = require('./style.css')

為了載入 CSS,我需要兩個 loader:

  1. css-loader 解析 CSS 內容
  2. style-loader 把 CSS 插入 DOM

接著到 config 裡面設定 rule

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = {
entry: './src/main.js',
mode: 'development',
output: {
path: __dirname,
filename: 'bundle.js'
},
// 在這裡設定
module: {
rules: [
{
// 所有 .css 結尾的檔案
test: /\.css$/i,
// 注意順序是「由後往前」,
// 先執行 css-loader 再執行 style.loader
use: ['style-loader', 'css-loader']
}
]
}
}

接著一樣 npm run build 打包,再載入 bundle.js 就完成了。

總之呢,基本規則都是這樣:

  1. 找出可以解析那個檔案格式的 loader
  2. 設定 config 中的 rules
  3. 打包

所以要載入 scss 的 config 就會這樣寫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module.exports = {
entry: './src/main.js',
mode: 'development',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
rules: [
{
// 所有 .sass 和 .scss 結尾的檔案
test: /\.s[ac]ss$/i,
/*
由後往前執行:
1. sass-loader
2. css-loader
3. style-loader
*/
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
}

認識 plug-in

webpack 本身也有一些能用的 plug-in,這邊拿 HtmlWebpackPlugin 來舉例。

簡單來說,這個 plug-in 是用來自動產生 HTML 檔。用的方式很簡單,安裝好後設定 config 就行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 引入 plug-in
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')

module.exports = {
entry: './src/main.js',
mode: 'development',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
// 設定 plug-in
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
})
]
}

template 可以指定樣板。簡單來說就是叫 webpack 用 src 底下的 HTML 來產生,這樣就能先寫好 HTML 的內容。

最後只要打包就會自動產生 index.html

webpack 開發時的一些設定 mentor-program-day77
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×