Webpack4學習筆記(四)——CSS處理
本文CSS%E5%A4%84%E7%90%86" target="_blank" rel="nofollow,noindex">github地址 。
最基本的CSS處理
Webpack最基本的css處理:css-loader + style-loader。其中css-loader用於處理css檔案中的@import
和url(...)
,而style-loader用於將css-loader的輸出生成js中的函式呼叫將css動態新增到html檔案中。
安裝css-loader和style-loader:
cnpm install --save-dev css-loader style-loader
首先建立index.html:
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>Webpack4-CSS處理-index</title> </head> <body> </body> </html>
然後建立main.css:
body { background-color: red; }
這裡我們將body設定成紅色。
然後在index.js檔案中直接importmain.css
:
import './main.css'
然後配置webpack.config.js,使webpack可以將css檔案當做module對待(即可以進行import操作)以及使用css-loader和style-loader對css檔案進行處理。
module.exports = { module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } ] } plugins:[ new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html', }) ] }
執行cnpm start
,可以看到紅色頁面出現。
抽離獨立的css檔案
style-loader將css動態新增到html檔案中,有時(特別是在生產環境下)我們希望將所有的css抽離為獨立的css檔案,此時可以藉助mini-css-extract-plugin
,安裝mini-css-extract-plugin
:
cnpm install --save-dev mini-css-extract-plugin
將style-loader改為mini-css-extract-plugin的loader:
... const MiniCssExtractPlugin = require("mini-css-extract-plugin"); ... { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] } ...
另外,還需要引入MiniCssExtractPlugin外掛本身:
... plugins: [ new CleanWebpackPlugin(['distribution']), new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html', }), new MiniCssExtractPlugin({ filename: "[name].[contenthash].css", chunkFilename: "[name].[contenthash].css" }) ] ...
執行cnpm run build
,可以看到生成了獨立的index..css檔案,另外所生成的index.
.html檔案中自動的引用了該css檔案。
CSS Module
css-loader支援CSS Module,此時需要修改webpack.config.js檔案中css-loader的配置:
... { loader: 'css-loader', options: { sourceMap: true, modules: true, localIdentName: '[name]---[local]---[hash:base64:5]' } }, ...
其中,modules: true,
表示啟用CSS Module,localIdentName
設定css類名的命名方式。
在main.css中加入更多的css:
body { background-color: red; } div { box-sizing: border-box; } .red { background-color:red; } :global(.gold){ background-color:gold; } :local(.aqua){ background-color: aqua; }
請注意這裡的:global(.gold)
和:local(.aqua)
,前者表示脫離模組化約束而使用全域性的宣告(即效果跟普通css相同),後者表示使用模組化化處理,這裡的:local
有點多餘,因為當啟用了CSS Module之後,css檔案中的類名預設便是:local
的。
執行cnpm run build
,輸出對應的css檔案為:
body { background-color: red; } div { box-sizing: border-box; } .main---red---1GVbX { background-color:red; } .gold{ background-color:gold; } .main---aqua---34_W6{ background-color: aqua; }
可以看到,.main---red---1GVbX
對應原檔案中的.red
,命名規則有localIdentName: '[name]---[local]---[hash:base64:5]'
配置完成。.gold
由於標記有:global
,所以生成的全域性的類名。
使用SASS
安裝sass-loader:
npm install sass-loader node-sass webpack --save-dev
請注意,要使用sass-loader,同時需要安裝node-sass。
修改webpack.config.js檔案,首先讓原來處理的css的loader也能處理scss檔案:
... test: /\.(s*)css$/, ...
然後加入sass-loader:
... { loader: 'sass-loader', options: { sourceMap: true } } ...
注意,sass-loader需要是處理scss/css檔案的第一個loader,也即需要配置在最後面。
為了演示sass的使用,建立partial的_global.scss:
$base-color:red;
然後將公共的css內容移動到base.scss:
body { background-color: red; } div { box-sizing: border-box; }
修改main.css檔案為main.scss,內容如下:
@import './global'; :global(.gold){ background-color:gold; } :local(.aqua){ background-color: aqua; } .localRed { background-color: $base-color; }
可以看到,在main.scss檔案中我們通過@import
引入了_global.scss
,然後對.localRed
應用了_global.scss
中的變數$base_color
。
作為演示,另外再常見main2.scss,情況與main.scss相似:
@import './global'; .red { background-color: red; } .localRed { background-color: $base-color; }
最後,在index.js檔案中引用base.scss,main.scss和main2.scss:
import './base.scss' import './main.scss' import './main2.scss'
執行cnpm run build
,輸出css檔案如下:
body { background-color: red; } div { box-sizing: border-box; } .gold { background-color: gold; } .main---aqua---Faovj { background-color: aqua; } .main---localRed---2p9T9 { background-color: red; } .main2---red---2xwE1 { background-color: red; } .main2---localRed---3yuRF { background-color: red; }
可以看到,生成的css檔案中有公共的base.scss中的內容,main.scss和main2.scss中的內容。
請注意,如果讓main.scss和main2.scss也引用base.scss,那麼body和div將出現3次,也即index.js、main.scss和main2.scss對base.scss的引用會重複出現,因為SASS分別以這三個檔案為編譯單位進行處理,最後將他們的內容合併到一起。因此,對於SASS來說,有個原則是如果有檔案作為公共css檔案,那麼建議它最多隻引用1次,通常是在入口檔案中進行引用,比如VueJS的App.vue檔案。
PostCSS
安裝postcss-loader:
cnpm install --save-dev postcss-loader
新增postcss-loader:
{ loader: 'postcss-loader', options: { plugins: [require("autoprefixer")], sourceMap: true } }
請注意,該loader的作用順序應該在sass-loader和css-loader之間。
這裡我們僅演示autoprefixer的使用,安裝autoprefixer:
cnpm install --save-dev autoprefixer
然後在package.json中新增:
"browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ]
此時的輸出檔案中,請注意base.scss中的div配置:
div { -webkit-box-sizing: border-box; box-sizing: border-box; }
其中的-webkit-box-sizing
為autoprefixer自動加上的字首。
多入口
當專案中有多個入口檔案時,webpack會為每個入口檔案生成對應的css bundle檔案。
為了演示真實場景,建立模組createElement.js,讓兩個入口檔案都依賴於該模組:
import style from "./createElement.scss"; export function createElement(className) { let element = document.createElement('div'); element.style.height = "50px"; element.classList.add(style[className]); element.style.marginBottom = "10px"; element.innerHTML = "Hello World."; document.body.appendChild(element); }
以及對應的scss檔案createElement.scss:
.blue { background-color: blue; } .yellow { background-color: yellow; } .green { background-color: green; } .gray { background-color:gray; } :global(.deepskyblue){ background-color:deepskyblue; } :local(.fuchsia){ background-color: fuchsia; }
修改index.js檔案,使其依賴於createElement.js:
import _ from 'lodash' import './base.scss' import './main.scss' import './main2.scss' import {createElement} from './createElement' createElement('yellow'); createElement('blue'); createElement('green');
增加index2.html檔案:
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>Webpack4-CSS處理-index2</title> </head> <body> </body> </html>
相應地增加index2.js檔案:
import _ from 'lodash' import './main.scss' import {createElement} from './createElement' createElement('fuchsia'); createElement('gray');
再次執行cnpm run build
,可以看到webpack為兩個入口檔案分別生成了css bundle檔案。
合併css檔案
有時我們需要將真個專案中所有生成的css合併為一個檔案,此時可以使用splitChunks:
splitChunks: { cacheGroups: { default: false, vendors: false, styles: { name: 'styles', test: /\.(s*)css$/, chunks: 'all', enforce: true } } }
修改HtmlWebpackPlugin配置,使每個入口檔案引用自己的bundle以及公共的styles chunk檔案:
new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html', chunks: ['index', 'manifest', 'vendors', 'styles'] }), new HtmlWebpackPlugin({ template: './src/index2.html', filename: 'index2.html', chunks: ['index2', 'manifest', 'vendors', 'styles'] }),
此時執行cnpm run build
,可以看到只有一個styles.*.css檔案生成。