在「煉金工房的核心設施」中,我們體驗了 postcss 的運作過程,不過大部分使用 postcss 的方式都是透過打包工具,打包工具跟 postcss 的流程差不多:
module.exports = ... JS 模組字串。
js 與 json。打包工具跟 postcss 主要差別在於應用層面:
postcss 主要用於處理 css。postcss 的工具,例如 babel 等。本篇要介紹的打包工具 webpack 引入了 loader 的概念,其他打包工具也都有類似的東西,只是名字可能叫做 plugin、transformer 等,本質上就是 JS 處理函式:
loader 還有一些延伸概念,例如處理執行流程的 Pitch-loader && Normal-loader、可在模組中使用的 inline-loader 等,就不額外展開了。
我們用下面這個範例來嘗試理解 postcss-loader:
package.json
{
"dependencies": {
"postcss": "^8.5.6"
},
"type": "module"
}
normal.css
/* hello world */
.apple {
color: yellow;
font-size: 100px;
}
main.js
postcss-loader 的原始碼沒有這麼簡單,這邊為了讓大家理解而簡化了。
import fs from 'fs'
import postcss from 'postcss'
// postcss plugin
/**
* @type {import('postcss').PluginCreator}
*/
const plugin = function () {
return {
postcssPlugin: ' :) ',
Comment: (comment) => {
comment.text = ':)'
},
Declaration: (decl) => {
if (decl.prop === 'color') {
decl.value = 'chocolate'
}
},
}
}
plugin.postcss = true
const RESOURCE_PATH = './normal.css'
// ---- 模擬 postcss-loader ----
function postcssLoader(source, callback) {
postcss([plugin])
.process(source, {from: RESOURCE_PATH})
.then((result) => {
callback(null, result.css)
})
}
// ---- 模擬 webpack 與 loader 的交互行為 ----
fs.readFile(RESOURCE_PATH, (_, data) => {
const cssString = data.toString()
// . 把讀到的數據傳給 loader
postcssLoader(cssString, (err, css) => {
if (err) {throw err}
// . 將 loader 傳出來的數據傳給下一個 loader
// . 直到最後會拿到一個 js script 來執行
console.log('[ Final Output ]')
console.log(css)
})
})
plugin:延續「煉金工房的核心設施」的範例,將註解改成 :)、顏色改成 chocolate。webpack 讀到數據後傳入 postcssLoader。postcssLoader 將數據交給 postcss。postcss 將數據傳入各個 plugin 處理。postcssLoader 將 postcss 處理完的數據返回給 webpack。webpack 繼續將數據傳給下一個 loader,直到最後一個 loader 處理完為止,並期望該 loader 返回的內容是 js 模組字串。結果
% node ./main.js
[ Final Output ]
/* :) */
.apple {
color: chocolate;
font-size: 100px;
}
postcss-loader 核心任務就是作為打包工具與 Postcss 之間的橋樑,此外 postcss-loader 還處理了 sourceMap 等其他附加資訊,就不額外展開了。
打包工具在應用的層面上,其實就是一堆設定的相互配合,梳理一下流程:
webpack 作為打包入口的 JS 文件。postcss 修改。webpack 設定檔:webpack.config.js,並且至少裝上 postcss-loader 來使用 postcss。postcss.config.js:postcss-loader 會自動查找項目根目錄的 postcss 設定檔。package.json
{
"devDependencies": {
"css-loader": "^7.1.2",
"mini-css-extract-plugin": "^2.9.4",
"postcss-load-config": "^6.0.1",
"postcss-loader": "^8.1.1",
"webpack": "^5.101.3",
"webpack-cli": "^6.0.1"
}
}
webpack
webpack:本體。webpack-cli:提供執行 webpack 指令的工具。css
postcss-loader:讓 webpack 使用 postcss 的中介層。postcss-load-config:postcss 設定檔的類型提示(可選)。css-loader:將 css 字串改成 JS 模組字串,例如:export default ".apple { color: red; }"。mini-css-extract-plugin:在 webpack 中處理 css 有兩種常見的方法:
css 塞到 HTML 的 <style>,此時我們會使用 style-loader 來處理。css 寫入一個檔案,就是使用 mini-css-extract-plugin。src/index.js
打包入口的 JS 文件。
import './normal.css'
src/normal.css
/* hi */
body {
color: blue;
}
postcss.config.js
詳細設定請看官方文件,最重要的是 plugins,用來 setup postcss plugin,也就是「煉金工房的核心設施」中,範例所寫的 postcss([plugin])。
const myPlugin = function ({txt = ':)', color = 'orange'} = {}) {
return {
postcssPlugin: 'my-plugin',
Comment: (comment) => {
comment.text = txt
},
Declaration: (decl) => {
if (decl.prop === 'color') {
decl.value = color
}
},
}
}
myPlugin.postcss = true
/** @type {import('postcss-load-config').Config} */
export default {
plugins: [
myPlugin({color: 'chocolate'}),
],
}
延續「煉金工房的核心設施」的範例,將 comment 改成 :)、color 改成 chocolate。
webpack.config.js
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import {join} from 'path'
export default {
entry: join(import.meta.dirname, './src/index.js'),
mode: 'production',
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
},
],
},
plugins: [new MiniCssExtractPlugin()],
}
entry:指定打包入口。mode:webpack 啟用模式。
production。development,編譯結果會有一堆有的沒的標記。module.rules
test 正規表示式時,就會將內容傳給 use 裡的 loader 處理。webpack loader 預設是從後往前執行的,所以是:
postcss-loader,將 css 交給 postcss 編譯成我想要的樣子。css-loader,將 css 編譯成 js 模組字串。MiniCssExtractPlugin.loader,將 js 模組字串攔截,後續讓插件處理。loader 有個 enforce 可以主動控制執行順序,就不展開說明了。plugins 在 webpack 中比較像是擴展 webpack 整體功能。
new MiniCssExtractPlugin 會將 MiniCssExtractPlugin.loader 攔截的內容寫入獨立的 css 檔案中。結果
% npx webpack && cat ./dist/main.css
# ...省略
/* :) */
body {
color: chocolate;
}
webpack 預設會打包到 dist/。MiniCssExtractPlugin 預設會寫入 main.css。:)、color 變成 chocolate 了以上就是如何在 webpack 中使用 postcss 的過程,不過近年除了 webpack 外,vite 是另一個常見的打包工具,下篇我們將介紹 postcss 在 vite 中的使用,我們下篇見囉~
postcss-loader 的維護團隊是 Webpack 官方維護團隊(webpack-contrib)。

有提示就是讚。
在 webpack 中,經常會使用 html-webpack-plugin 插件來生成 html 文件,他的作用是:
<script/> 或 <link/> 來引入這些打包後的資源。延續上面的範例:
下載
npm i html-webpack-plugin -D
webpack.config.js
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import {join} from 'path'
import HtmlWebpackPlugin from 'html-webpack-plugin'
export default {
entry: join(import.meta.dirname, './src/index.js'),
mode: 'production',
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
},
],
},
plugins: [new HtmlWebpackPlugin({template: join(import.meta.dirname, './index.html')}), new MiniCssExtractPlugin()],
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
hi :)
</body>
</html>
其他都沒變
結果
% npx webpack && ls -l dist && cat ./dist/index.html
-rw-r--r--@ 1 jz staff 153 Sep 27 17:57 index.html
-rw-r--r--@ 1 jz staff 39 Sep 27 17:57 main.css
-rw-r--r--@ 1 jz staff 0 Sep 27 17:57 main.js
<!doctype html><html lang="en"><head><script defer="defer" src="main.js"></script><link href="main.css" rel="stylesheet"></head><body>hi :)</body></html>
生成的 html 注入了其他打包資源的標籤,這就是 webpack 最基礎的完整使用流程,為了 webpack 篇的完整性,額外分享這個常用插件給你~