1. 程式人生 > >手把手教你webpack3(7)style-loader詳細使用說明

手把手教你webpack3(7)style-loader詳細使用說明

STYLE-LOADER詳細使用說明

前注:

如果可以,請給本專案加【Star】和【Fork】持續關注。

有疑義請點選這裡,發【Issues】。

DEMO地址

1、概述

簡單來說,style-loader是將css-loader打包好的css程式碼以<style>標籤的形式插入到html檔案中。

對於簡單專案,打包然後插入也就足夠了,但是遇見覆雜情況,例如:

  1. 需要使用webpack的伺服器熱載入服務進行特殊配置;
  2. 對css檔案二次處理(更改類名,新增額外css屬性之類);
  3. 合併 <style> 標籤(預設是不合並的);
  4. 啟用sourceMap等(雖然實際根本無效嘛);
  5. 路徑轉換(相對路徑轉為絕對路徑);
  6. <style> 標籤新增自定義屬性;
  7. 手動掛載、移除 <style> 標籤等;

顯然就不行了。

所以需要通過配置來進行設定。

2、配置

有幾個屬性需要和其他東西(比如某些loader)配合,才能生效。

所以先介紹功能明確的幾個,再簡述很難直接應用的幾個。

2.0、普通

匯入方式有兩種

  1. 直接import 'foo.css'
  2. es6語法 import foo from 'foo.css';

前者沒啥好說的。

當使用後者匯入時,有一些特殊特性:

  1. 在使用區域性作用域時(css-loadermodules屬性的應用),會有生成的(區域性)識別符號(identifier)。
    ,可以通過foo.className來獲取
  2. 當使用useable特性時,可以通過foo.use()以及foo.unuse()來讓css生效/失效;
  3. url 特性嘗試失敗。

2.1、attrs

名稱 型別 預設值 描述
attrs {Object} {} 新增自定義 attrs 到 style 標籤

attrs 屬性最好理解。

  1. attrs的值是一個物件;
  2. 物件 key , val 成對出現的;
  3. 插入形式是以 key=val的形式插入;
  4. 但例如 [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標籤

簡單來說,insertAtinsertInto 共通決定style標籤插入哪裡。

兩種情況:

  1. insertAt 值為 string 型別。可以是 top 或者 bottom,表示插入某個標籤 的頂部或者結尾,和該標籤是父子關係;
  2. insertAt 值為 object 型別。key只能是 before(見 node_modules/style-loader/lib/addStyles.js 第173行),表示插入到某個標籤之前(和該標籤是兄弟關係),例如以下:
insertAt: {
    before: '#app'
},
insertInto: 'body'

以上程式碼表示,先在<body> 標籤能找到<div id='app'></div>這個標籤,然後插入到這個標籤之前;

假如找不到符合要求的標籤,則預設插入到 <head></head> 標籤的末尾。

整個插入邏輯如下:

  1. 假如 insertAt 是值是 top 或者 bottom ,那麼 style 標籤將插入到 insertInto 所指向的DOM(通過document.querySelector(target)獲取)的開頭或末尾( style 標籤為指向DOM的子元素);
  2. 假如 insertAt 的值是物件,那麼則插入 insertInto 的子元素的 insertAt.before 所指向的DOM之前(即 document.querySelector("insertInto insertAt.before") 指向的DOM)。

注意,兩個屬性的標籤選擇器,都是通過 document.querySelector 實現的,所以存在兩個問題:

  1. 屬性的值,需要符合 document.querySelector 的語法;
  2. 低版本瀏覽器(比如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-loadersourceMap屬性賦值的(我沒有去跟原始碼,但測試後推斷就是這樣的)。

其次,從相對路徑轉為絕對路徑,是在前端通過js程式碼轉換的。

第三,convertToAbsoluteUrls 的效果如官方描述一樣,具體下面舉例。

幾種情況如下(截止style-loader版本0.19.0):

  1. convertToAbsoluteUrls 值為false時,依然使用相對路徑,即例如./foo.png
  2. css-loadersourceMap 的值為true,且convertToAbsoluteUrls 值為true時,更改為絕對路徑,即例如http://127.0.0.1:8080/foo.png
  3. 可能是bug,所以目前不受style-loadersourceMap屬性的影響;

bug的demo如連結

2.5、useable

作用

讓所有引入的css檔案,變為手動載入;

使用方法

loader: 'style-loader/useable'

說明:

  1. 當使用這個特性時,transform屬性失效(打包正常,但掛載的時候表示會報錯)
  2. 示例程式碼如下,效果解釋:
  3. 用一個變數標記當前是否掛載,點選後進行判斷;
  4. 如果掛載了,通過style.unref()從DOM樹中移出(這些樣式會失效);
  5. 如果沒有掛載,那麼通過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
    }
}

說明:

  1. 預設情況下,我們會發現,每一個css檔案會變為一個<style>標籤,因此實際載入中,可能有多個<style>標籤;
  2. 預設值為 false (而不是原文中說的預設情況下啟用此選項
  3. 當該值設定為 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 是什麼再說吧,略略略。