背後靈魂 webpack (2) - 配置 CSS loader & plugin

集點送紅利 / Hiro
UNNO Technology
Published in
9 min readJun 16, 2020

上回講到了 webpack 基本配置,這回要從 webpack 的一些進化元素,loaderplugin 開始講起,而且方向是跟 CSS 編譯相關的!

在開頭會先提到一個檔名的優化配置。

目錄

  • 優化配置 - 檔名變化
  • plugin & loader 介紹
  • CSS 編譯
    - css-loader & style-loader
    - mini-css-extract-plugin
    - file-loader
    - 圖片路徑錯誤

優化配置 - 檔名變化

回顧一下上回編譯的 webpack.config.js 配置:

當我們每次編譯後,都會出現一直名為 bundle.js 的 JS 檔,但…如果今天我的 entry 想要不只一個檔案的話該怎麼辦呢?

我們先來新增第二隻 utils.js

webpack-demo
|- /src
|- index.js
+ |- utils.js

內容隨便寫個:

const mutiply = (x, y) => x * y

接著我們在 webpack.config.js 裡調整一下:

我們把 entry 改寫為物件,filename 變為 [name].js,這邊的 [name] 會對應到 entrykey 名稱,這時候我們再編譯一次:

就可以看到 entry 變為兩個而且編譯出兩個檔案了!

注意! [name] 這個名稱標籤接下來會頻繁出現。

接下來我們直接進入 loader & plugin 吧。

loder & plugin 介紹

  • loader
    loader 存在的原因是為了讓 webpack 可以讀取不同的程式碼。
  • plugin
    plugin 則是豐富了 webpack 的功能

CSS 編譯

css-loader & style-loader

首先來小試身手,試著編譯 CSS 檔案。編譯 CSS 檔常伴隨著兩個 loadercss-loader 做的是解析 CSS,style-loader 則是做編譯匯出的部分。

照著安裝步驟:

$ npm install --save-dev css-loader style-loader

接著一樣在 webpack.config.js 加上以下設定:

這邊要講一下其實 use 陣列裡是有順序的,從右至左,先使用 css-loader 做 CSS 解析,再使用 style-loader 編譯。

我們在資料夾下創一個 index.css

webpack-demo
|- /src
|- index.js
|- utils.js
+ |- /css
+ |- index.css

並且寫上一點 CSS:

body {
background: red;
}

在我們設定的 entry ,也就是 index.js 內引入 CSS:

const index = require('./css/index.css')

這樣我們就只需要做最後一步:

$ npm run build

打開 dist/bundle.js,可以看到剛剛寫的 CSS 已經透過引入被編譯出來:

CSS 透過 style-loader 編譯進 JS 檔

但…我寫的是 CSS 怎麼會被編譯到 JS 檔裡呢?

其實根據不同需求,可以選擇你要不要插在 JS 檔裡或是把 CSS 抽出來(那個…我懷疑你在開車,但我沒有證據

抽出的話網頁就會多請求一個檔案,沒抽出下 JS 檔就會比較肥大。當然,這時候要介紹個常伴隨 CSS 編譯的一個 plugin

mini-css-extract-plugin

這個 plugin 就是幫助我們在編譯時抽離 CSS,舊專案的話你可能會比較常看到 Extract Text Plugin 這個,兩個做的事情是一樣的,只是目前後者在慢慢淘汰。

直接來看安裝方法:

$ npm install --save-dev mini-css-extract-plugin

接著引入 webpack.config.js,並且把 style-loader 替換掉:

mini-css-extract-plugin

再重新編譯一次,就可以看到 dist 資料夾內跑出 CSS 檔啦!

因為剛剛 output 的設置,檔案名稱會自動對應到我們在 entry 檔案的 key,我們在 index.js 引入 CSS 的,所以名稱就對應到 index.css

檔案名稱對應 ObJect 的 Key

file-loader

在講這個 loader 前,我們先在資料夾內新增圖片:

webpack-demo
|- /src
|- index.js
|- utils.js
|- /css
|- index.css
+ |- /assets
+ |- demo.png

並且在 CSS 內引入:

body {
background-image: url('/src/assets/demo.png')
}

並且下指令編譯後,可以看到我們成功編譯出 CSS,但路徑跟上面一樣還指著 src 開發用資料夾。

從上回看到這邊可以隱約發現一個通用習慣:
開發時都用 src 資料夾來存放檔案,透過編譯才變成 dist 一包。

所以…統整一下現在遇到的問題:

  1. CSS 路徑一樣是指向 src 開發用資料夾
  2. 就算改了路徑,圖片也依然存在於 src 資料夾裡

那如果把路徑改為相對路徑來編譯看看呢?

body {
background-image: url('../assets/demo.png')
}

這時後 webpack 就會噴紅字給你看了!

發現了不知道如何讀取的檔案

那剛剛的絕對路徑為啥沒有爆呢?原因在 css-loader 上,翻一下文件可以看到他是不會去解析絕對路徑或是路徑以 / 開頭的。

css-loader url

所以…為了讓 webpack 解析相對路徑,還記得文章一開始提到的 loader 嗎?

loader 存在的原因是為了讓 webpack 可以讀取不同的程式碼。

也就是說我們少了一個可以讓 webpack 讀取圖片的 loader,接下來就是讓 file-loader 出場的時候了:

$ npm install file-loader --save-dev

webpack.config.js 設定檔:

file-loader config

這時候再編譯一次,會發現圖片成功透過 webpack 編譯並且搬進了 dist 資料夾,而且連 CSS 裡面的路徑都變了:

這邊來小總結一次:

  1. CSS 檔案先透過 css-loader 解析 CSS 語法
  2. 遇到相對路徑後交由 file-loader 來解析,並且把檔案搬到目標 dist 資料夾
  3. 最後再由 mini-css-extract-plugin 去修正圖片的路徑。

圖片路徑錯誤

這邊要另外補充一點跟圖片有關的,如果在 mini-css-extract-plugin 內設定過 filename,而且 CSS 不是編譯在 dist 根目錄的話:

mini-css-extract-plugin filename set

路過一下,好奇 filenamechunkFilename 可以參考延伸閱讀哦!

這時候打開的會是這樣:

會發現根本吃不到圖片,這也是在打包部署常出現的一件事!

因為默認情況下,圖片會是相對 dist 資料夾的根目錄,所以要去更改 publicPath 這個屬性。

mini-css-extract-plugin publicPath set

因為 CSS 檔案包在 CSS 資料夾裡面,需要在前面加上 ../ 讓路徑先回到 dist,再重新編譯一次就好了!

延伸閱讀

fileName & chunkFileName

  • filename 為一般列在 entry 檔內打包出來的檔案。
  • chunkFilename 為未被列在 entry 檔內但也需要打包的檔案。

結論

這回我們總共講了:

  • 使用 [name] 名稱標籤,來做多個 entry 檔案
  • css-loader & style-loader
  • mini-css-extract-plugin
  • file-loader
  • CSS 圖片路徑錯誤

這次寫 webpack 發現好多關聯性實在太高了,有些又要排順序…對於不太會計畫的我實在是有點痛苦 XD

到目前我們還有一個不太優的配置是:

  • 每次編譯都要手動改 index.html 的路徑連結

第三篇文章 「背後靈魂 webpack (3) — html-webpack-plugin & devServer 」會針對這個問題點來做優化!

參考資料

--

--

UNNO Technology
UNNO Technology

專注解決大型網站架構所產生的高併發、高可用問題,後端採用 Python Django Framework 並以 RESTful API 形式溝通前後端資料,Client 端透過 Vue 快速搭建 WEB 端、React Native 撰寫 iOS / Android 兩作業系統,深度使用 GCP 相關架站、流量監控、k8s 容器管理等應用。

集點送紅利 / Hiro
集點送紅利 / Hiro