前言
大傢伙都清楚在使用webpack
構建前端專案時都會使用到sass-loader、less-loader、postcss-loader、css-loader、style-loader
,但這些loader
在其中起到什麼作用呢?本篇主要闡述css-loader
與style-loader
的作用和實現,加深對loader
的理解。
css-loader
css-loader
會對 @import
和 url()
進行處理,就像 js
解析 import/require()
一樣,預設生成一個陣列存放存放處理後的樣式字串,並將其匯出。
假如有三個樣式檔案:a.module.scss
,b.module.scss
,c.module.scss
,它們之間的依賴關係是這樣的:
// a.module.scss
@import './b.module.scss';
.container {
width: 200px;
height: 200px;
background-color: antiquewhite;
}
// b.module.scss
@import url('./c.module.scss');
.text {
font-size: 16px;
color: #da2227;
font-weight: bold;
}
// c.module.scss
.name {
font-size: 16px;;
color: #da7777;
font-weight: bold;
}
在index.jsx
檔案中引入它們
// index.jsx
import React from 'react';
import styles from './a.module.scss';
const Index = () => {
return <div className={styles.container}>
<span className={styles.text}><span className={styles.name}>xxx,</span>hello world!!!</span>
</div>
}
export default Index;
webpack.config.js
構建指令碼
// webpack.config.js
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {
index: './src/index.jsx'
},
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, './dist'),
library: 'MyComponent',
libraryTarget: 'umd',
},
resolve: {
extensions: ['.js', '.jsx', '.tsx'],
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: false, // 禁止css modules
}
},
'sass-loader'
]
},
{
test: /\.(jpg|jpeg|png|gif)$/,
use: ['url-loader']
}
]
},
plugins: [
new webpack.ProgressPlugin(),
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ['dist']
}),
],
}
.babelrc
配置
// .babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
在package.json
配置構建命令"build:dev": "webpack --mode none"
。為了方便分析構建之後的程式碼,這裡將mode
設定為none
。
三種mode
方式:
development | 會將 DefinePlugin 中 process.env.NODE_ENV 的值設定為 development 。為模組和 chunk 啟用有效的名,也就是將模組id替換成模組名稱。 |
---|---|
production | 會將 DefinePlugin 中 process.env.NODE_ENV 的值設定為 production 。為模組和 chunk 啟用確定性的混淆名稱, FlagDependencyUsagePlugin ,FlagIncludedChunksPlugin ,ModuleConcatenationPlugin ,NoEmitOnErrorsPlugin 和 TerserPlugin 。 |
none | 不使用任何預設優化選項 |
執行yarn build:dev
構建,分析生成的檔案
(1)a.module.scss
使用@import
引入了b.module.scss
,被處理放入同一個模組的陣列___CSS_LOADER_EXPORT___
中並匯出
(2)b.module.scss
使用@import url()
引入了c.module.scss
,但c.module.scss
被單獨處理放入另一個模組的陣列___CSS_LOADER_EXPORT___
中並匯出
(3)a.module.scss
與b.module.scss
被處理放入一個模組陣列,c.module.scss
被單獨處理放入另一個模組的陣列,但是b.module.scss
與c.module.scss
是由引入關係的,這個在構建後怎麼關聯依賴的呢?
a.module.scss
與b.module.scss
被處理放入模組id
為12
的陣列,c.module.scss
被處理放入模組id
為15
的陣列,模組id
為12
的模組中匯入了模組id
為15
中的樣式陣列,並將其追加到模組id
為12
的陣列中,最後統一以陣列__WEBPACK_DEFAULT_EXPORT__
匯出,css-loader
處理到這一步就結束了。
另外css-loader
還提供其他的功能,比如css modules
,想要了解可以參照例子開啟css modules
構建,窺其原理,此處不作介紹
css-loader
匯出方式
上面說到引入的樣式都被轉化成樣式字串放入模組陣列中,這是預設的處理方式,其實還有另外兩種。
配置項exportType
允許匯出樣式為'array'
、'string'
或者 'css-style-sheet'
可構造樣式(即 CSSStyleSheet
), 預設值:'array'
CSSStyleSheet
介面代表一個 CSS 樣式表,並允許檢查和編輯樣式表中的規則列表。它從父型別 StyleSheet
繼承屬性和方法。
一個 CSS
樣式表包含了一組表示規則的 CSSRule
物件。每條 CSS
規則可以通過與之相關聯的物件進行操作,這些規則被包含在 CSSRuleList
內,可以通過樣式表的 cssRules
(en-US) 屬性獲取。
例如,CSSStyleRule
物件中的一條規則可能包含這樣的樣式:
h1, h2 {
font-size: 16pt;
}
style-loader
style-loader
的作用是把 CSS
插入到 DOM
中,就是處理css-loader
匯出的模組陣列,然後將樣式通過style
標籤或者其他形式插入到DOM
中。
配置項injectType
可配置把 styles
插入到 DOM
中的方式,主要有:
styleTag
:通過使用多個<style></style>
自動把styles
插入到DOM
中。該方式是預設行為singletonStyleTag
:通過使用一個<style></style>
來自動把styles
插入到DOM
中autoStyleTag
:與styleTag
相同,但是當程式碼在IE6-9
中執行時,請開啟singletonStyleTag
模式lazyStyleTag
:在需要時使用多個<style></style>
把styles
插入到DOM
中。推薦lazy style
遵循使用.lazy.css
作為字尾的命名約定,style-loader
基本用法是使用.css
作為檔案字尾(其他檔案也一樣,比如:.lazy.less
和.less
)。當使用lazyStyleTag
時,style-loader
將惰性插入styles
,在需要使用styles
時可以通過style.use()
/style.unuse()
使style
可用。lazySingletonStyleTag
:通過使用一個<style></style>
來自動把styles
插入到DOM
中,如上提供惰性支援lazyAutoStyleTag
:與lazyStyleTag
相同,但是當程式碼在IE6-9
中執行時,請開啟lazySingletonStyleTag
模式linkTag
:使用多個<link rel="stylesheet" href="path/to/file.css">
將 styles 插入到 DOM 中。此loader
會在執行時使用JavaScript
動態地插入<link href="path/to/file.css" rel="stylesheet">
。要靜態插入<link href="path/to/file.css" rel="stylesheet">
時請使用MiniCssExtractPlugin。
我們以styleTag
插入方式進行分析:
上面說到所有的樣式都追加到模組id
為12
的模組陣列中,下面先獲取模組id
為12
的模組陣列,然後生成style
標籤將其樣式插入DOM
中
上圖中_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()
得到的就是下圖模組返回的update
方法,該方法呼叫了其他很多方法將樣式通過style
標籤插入DOM
參考: