手把手教你webpack3(7)style-loader詳細使用說明
STYLE-LOADER詳細使用說明
前注:
如果可以,請給本專案加【Star】和【Fork】持續關注。
有疑義請點選這裡,發【Issues】。
DEMO地址
1、概述
簡單來說,style-loader
是將css-loader
打包好的css程式碼以<style>
標籤的形式插入到html檔案中。
對於簡單專案,打包然後插入也就足夠了,但是遇見覆雜情況,例如:
- 需要使用webpack的伺服器熱載入服務進行特殊配置;
- 對css檔案二次處理(更改類名,新增額外css屬性之類);
- 合併
<style>
標籤(預設是不合並的); - 啟用sourceMap等(雖然實際根本無效嘛);
- 路徑轉換(相對路徑轉為絕對路徑);
- 給
<style>
標籤新增自定義屬性; - 手動掛載、移除
<style>
標籤等;
顯然就不行了。
所以需要通過配置來進行設定。
2、配置
有幾個屬性需要和其他東西(比如某些loader)配合,才能生效。
所以先介紹功能明確的幾個,再簡述很難直接應用的幾個。
2.0、普通
匯入方式有兩種
- 直接
import 'foo.css'
; - es6語法
import foo from 'foo.css'
;
前者沒啥好說的。
當使用後者匯入時,有一些特殊特性:
- 在使用區域性作用域時(
css-loader
的modules
屬性的應用),會有生成的(區域性)識別符號(identifier)。
foo.className
來獲取 - 當使用
useable
特性時,可以通過foo.use()
以及foo.unuse()
來讓css生效/失效; url
特性嘗試失敗。
2.1、attrs
名稱 | 型別 | 預設值 | 描述 |
attrs | {Object} | {} | 新增自定義 attrs 到 style 標籤 |
attrs
屬性最好理解。
attrs
的值是一個物件;- 物件 key , val 成對出現的;
- 插入形式是以
key=val
的形式插入; - 但例如
[name]
或者[hash]
之類的,無效;
例如:
{
loader: 'style-loader',
options: {
attrs: {
id: 'foo'
}
}
}
插入到html後,style標籤變為如下形式:<style id="foo" type="text/css">css程式碼略</style>
。
但是缺點是不能變為雜湊值,所以如果想要實現css的區域性作用域,還需要其他東西配合(這裡略略略)。
2.2、transform
名稱 | 型別 | 預設值 | 描述 |
transform | {Function} | false | 轉換/條件載入 CSS,通過傳遞轉換/條件函式 |
簡單來說,這個是拿到以字串的形式拿到css檔案,然後將這個字串以引數的形式傳給處理函式,函式處理完後返回,返回值即實際插入style標籤的內容。
使用方法:
1、配置 style-loader
的屬性如下:
{
loader: 'style-loader',
options: {
transform: 'transform.js' // 可以使用相對路徑,這裡表示跟 webpack.config.js 同目錄
}
}
2、在webpack.config.js
的同一個目錄下建立檔案:transform.js
(即上面寫的那個路徑)
3、在transform.js
檔案內,貼上如下程式碼(CommonJS模組形式):
// 這裡只有一個引數,即css字串
module.exports = function (css) {
console.log(css)
const transformed = css.replace(/}/g, 'box-sizing: border-box;\n}')
return transformed
}
這段程式碼的作用,相當於給每個css樣式裡,添加了一個box-sizing: border-box;
屬性。
例如css檔案如下:
// foo.css 轉換前
#app {
position: relative;
}
轉換完後的結果變為:
// foo.css 轉換後
#app {
position: relative;
box-sizing: border-box;
}
box-sizing:
前面沒有空格,是因為轉換函式裡,replace第二個引數的box-sizing:
前沒有空格。
4、每個css檔案都會執行一次這段程式碼。css字串,不包含@import
匯入的css檔案相關的幾行程式碼;
5、重要:這段程式碼執行的時間不在打包的時候,而是在插入到html檔案中的時候。
這意味著你可以取得一些根據當前瀏覽器環境設定的值。例如通過document.body.clientWidth
拿到瀏覽器寬度,然後動態計算一些css屬性是否插入到頁面中(響應式).
應用:
當以字串形式拿到css程式碼的時候,我們可以做很多事情。我舉幾個例子:
1、判斷當前瀏覽器環境,當需要額外相容程式碼的時候,給css屬性新增相容性程式碼。
例如遇見box-sizing
,新增-webkit-box-sizing:
和-moz-box-sizing:
。
2、可以進行風格設定。
例如同時存在亮色和暗色風格,使用者使用的風格在設定後存在cookies或者localStorage,那麼寫兩套程式碼顯然是比較麻煩的。
就可以引入一個顏色對映表(暗色的值->亮色的值),預設使用暗色的值。
當檢查到使用者使用亮色風格時(讀取cookies或者localStorage),通過顏色對映表,利用 replace
函式,將顏色值替換為亮色的。
2.3、insertAt和insertInto
名稱 | 型別 | 預設值 | 描述 |
insertAt | {String|Object} | bottom | 在給定位置處插入style標籤 |
insertInto | {String} | 給定位置中插入style標籤 |
簡單來說,insertAt
和 insertInto
共通決定style標籤插入哪裡。
兩種情況:
insertAt
值為string
型別。可以是top
或者bottom
,表示插入某個標籤 內 的頂部或者結尾,和該標籤是父子關係;insertAt
值為object
型別。key只能是before
(見node_modules/style-loader/lib/addStyles.js
第173行),表示插入到某個標籤之前(和該標籤是兄弟關係),例如以下:
insertAt: {
before: '#app'
},
insertInto: 'body'
以上程式碼表示,先在<body>
標籤能找到<div id='app'></div>
這個標籤,然後插入到這個標籤之前;
假如找不到符合要求的標籤,則預設插入到 <head></head>
標籤的末尾。
整個插入邏輯如下:
- 假如
insertAt
是值是top
或者bottom
,那麼style
標籤將插入到insertInto
所指向的DOM(通過document.querySelector(target)
獲取)的開頭或末尾(style
標籤為指向DOM的子元素); - 假如
insertAt
的值是物件,那麼則插入insertInto
的子元素的insertAt.before
所指向的DOM之前(即document.querySelector("insertInto insertAt.before")
指向的DOM)。
注意,兩個屬性的標籤選擇器,都是通過 document.querySelector
實現的,所以存在兩個問題:
- 屬性的值,需要符合
document.querySelector
的語法; - 低版本瀏覽器(比如IE)可能不支援這個選擇器API;
2.4、sourceMap和convertToAbsoluteUrls
名稱 | 型別 | 預設值 | 描述 |
sourceMap | {Boolean} | false | 啟用/禁用 Sourcemap |
convertToAbsoluteUrls | {Boolean} | false | 啟用 source map 後,將相對 URL 轉換為絕對 URL |
首先,sourceMap
實測和翻原始碼後,感覺沒有生效。
在跟了一遍程式碼後,推測原因在於,sourceMap的取值,取的是 css-laoder
的sourceMap的值。
準確的說,在webpack裡,css檔案被視為一個模組,因此import
引入的css檔案,也是一個模組物件。而在判斷的時候,取的是這個模組(是一個object)的屬性sourceMap的值,而不是 options.sourceMap
的值。
我已經提了issues給官方了。
而這個模組的值,推測是被css-loader
的sourceMap
屬性賦值的(我沒有去跟原始碼,但測試後推斷就是這樣的)。
其次,從相對路徑轉為絕對路徑,是在前端通過js程式碼轉換的。
第三,convertToAbsoluteUrls
的效果如官方描述一樣,具體下面舉例。
幾種情況如下(截止style-loader
版本0.19.0
):
- 當
convertToAbsoluteUrls
值為false時,依然使用相對路徑,即例如./foo.png
; - 當
css-loader
的sourceMap
的值為true,且convertToAbsoluteUrls
值為true時,更改為絕對路徑,即例如http://127.0.0.1:8080/foo.png
; - 可能是bug,所以目前不受
style-loader
的sourceMap
屬性的影響;
bug的demo如連結
2.5、useable
作用
讓所有引入的css檔案,變為手動載入;
使用方法
loader: 'style-loader/useable'
說明:
- 當使用這個特性時,
transform
屬性失效(打包正常,但掛載的時候表示會報錯) - 示例程式碼如下,效果解釋:
- 用一個變數標記當前是否掛載,點選後進行判斷;
- 如果掛載了,通過
style.unref()
從DOM樹中移出(這些樣式會失效); - 如果沒有掛載,那麼通過
style.ref()
插入到DOM樹中進行掛載(樣式生效)
程式碼:
// app.js
import style from './style/style.css'
/* useable(開始) */
let isUse = false
// 這是一個按鈕的點選事件
document.querySelector('#test').onclick = function () {
if (isUse) {
style.unref()
} else {
style.ref()
}
isUse = !isUse
}
/* useable(結束) */
2.6、singleton
作用
使用一個的 <style>
標籤載入所有css屬性,或者是每個css模組一個 <style>
標籤。
預設值是false(每個css模組一個 <style>
標籤);
使用方法
{
loader: 'style-loader',
options: {
singleton: true
}
}
說明:
- 預設情況下,我們會發現,每一個css檔案會變為一個
<style>
標籤,因此實際載入中,可能有多個<style>
標籤; - 預設值為
false
(而不是原文中說的預設情況下啟用此選項
) - 當該值設定為
true
時,那麼,原本多個<style>
標籤會被合併成一個<style>
標籤。
注:
官方文件上寫的是預設開啟,實際上是不開啟的(v0.19.0)。我已經提了issues
2.7、hmr
名稱 | 型別 | 預設值 | 描述 |
hmr | {Boolean} | true | Enable/disable Hot Module Replacement (HMR), if disabled no HMR Code will be added (good for non local development/production) |
谷歌翻譯為中文:
啟用/禁用熱模組更換(HMR),如果禁用,則不會新增HMR程式碼。 這可以用於非本地開發和生產。
大概就是指預設為true時,允許熱載入(就是你改了程式碼後,不需要重新整理頁面,立刻更新資料),我沒實際測試過,不過應該沒人會把這個設定為false吧?
2.8、base
名稱 | 型別 | 預設值 | 描述 |
base | {Number} | true | 設定模組 ID 基礎 (DLLPlugin) |
當使用一個或多個 DllPlugin 時,此設定主要用作 css 衝突 的修補方案。base 可以防止 app 的 css(或 DllPlugin2 的 css)覆蓋 DllPlugin1 的 css,方法是指定一個 css 模組的 id 大於 DllPlugin1 的範圍,例如:
等我搞清楚 DllPlugin 是什麼再說吧,略略略。