淺析css-loader和style-loader的作用、css-loader和style-loader是如何配合使用的、less-loader和css-loader和style-loader的實現原理


  大家都清楚在使用webpack構建前端項目時都會使用到sass-loader、less-loader、postcss-loader、css-loader、style-loader,但這些loader在其中起到什么作用呢?本篇主要闡述css-loader與style-loader的作用和實現,加深對loader的理解。

  style-loader和css-loader作用是不同的。

1、css-loader: 加載.css文件

2、style-loader:使用<style>將css-loader內部樣式注入到我們的HTML頁面

一、css-loader的作用

1、先講css-loader的作用:css-loader是幫助webpack打包處理css文件的工具

2、css-loader 使用注意項:

(1)使用css-loader必須要配合使用style-loader

(2)css-loader的作用是幫我們分析出各個css文件之間的關系,把各個css文件合並成一段css

(3)style-loader的作用是將css-loader生成的css代碼掛載到頁面的header部分

(4)多個loader配合使用時,處理順序是:從下到上,從右到左 的順序;

3、由於 webpack 只能處理js相關的文件,所以像圖片和css資源是處理不了的,css-loader的作用是將css文件轉換成webpack能夠處理的資源,而style-loader就是幫我們直接將css-loader解析后的內容掛載到html頁面當中

二、css-loader和style-loader是如何配合使用的?

  webpack是用JS寫的,運行在node環境,所以默認webpack打包的時候只會處理JS之間的依賴關系!

  因為像 .css 這樣的文件不是一個 JavaScript 模塊,你需要配置 webpack 使用 css-loader 或者 style-loader 去合理地處理它們。

  css-loader會遍歷css文件,找到所有的url(...)並且處理。style-loader會把所有的樣式插入到你頁面的一個style

1、如果在JS中導入了css,那么就需要使用 css-loader 來識別這個模塊,通過特定的語法規則進行轉換內容最后導出

  css-loader 會對 @import 和 url() 進行處理,就像 js 解析 import/require() 一樣,默認生成一個數組存放存放處理后的樣式字符串,並將其導出。

// base.css
.bg { background: #000; } const style = require('./base.css') console.log(style, 'css')

  css-loader處理之后導出的是

  但是這並不是我們想要的,因為是個數組,頁面是無法直接使用,這時我們需要用到額外一個style-loader來處理。

2、style-loader 是通過一個JS腳本創建一個style標簽,里面包含一些樣式。style-loader是不能單獨使用的,因為它並不負責解析 css 之前的依賴關系,每個loader的功能都是單一的,各自拆分獨立。

  style-loader的作用是把 CSS 插入到 DOM 中,就是處理css-loader導出的模塊數組,然后將樣式通過style標簽或者其他形式插入到DOM中。

  配置項injectType可配置把 styles 插入到 DOM 中的方式。

三、css-loader和style-loader的使用方式

1、內聯方式(不常用)如:import "css-loader!../css/style.css"

  在導入的樣式前加上使用的loader,並且使用 !分隔,此方法不常用

2、cli 方式:已棄用

3、配置方式:在webpack.config.js文件中寫明配置信息,module.rules中可以配置多個loader

const path = require('path'); module.exports = { entry: "./src/main.js",//入口文件
  output: {//出口文件
    path: path.resolve(__dirname, "build"), filename:"bundle.js" }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader' ] } ] } }

  注意:寫法是有順序的

四、less-loader

  Less是CSS預處理語言,擴展了CSS語言,增加了變量、Mixin、函數等特性,Less-loader的作用就是將less代碼轉譯為瀏覽器可以識別的CSS代碼。

  所以less-loader的原理很簡單,就是調用less庫提供的方法,轉譯less語法后輸出,如下:

// less-loader實現(經簡化)
const less = require('less'); module.exports = function(content) { const callback = this.async(); // 轉譯比較耗時,采用異步方式
  const options = this.getOptions(); // 獲取配置文件中less-loader的options
 less.render( content, createOptions(options), // less轉譯的配置
    (err, output) => { callback(err, output.css); // 將生成的css代碼傳遞給下一個loader
 } ); };

五、css-loader

  css-loader的作用主要是解析css文件中的@import和url語句,處理css-modules,並將結果作為一個js模塊返回。

  假如我們有a.css、b.css、c.css:

// a.css
@import './b.css'; // 導入b.css .a { font-size: 16px; } // b.css
@import './c.css'; // 導入c.css .b { color: red; } // c.css
.c { font-weight: bolder; }

  來看看css-loader對a.css的編譯輸出:

// css-loader輸出 exports = module.exports = require("../../../node_modules/css-loader/lib/css-base.js")(false); // imports // 文件需要的依賴js模塊,這里為空 // module
exports.push([ // 模塊導出內容
 module.id, ".src-components-Home-index__c--3riXS {\n font-weight: bolder;\n}\n.src-components-Home-index__b--I-yI3 {\n color: red;\n}\n.src-components-Home-index__a--3EFPE {\n font-size: 16px;\n}\n", "" ]); // exports
exports.locals = { // css-modules的類名映射
  "c": "src-components-Home-index__c--3riXS", "b": "src-components-Home-index__b--I-yI3", "a": "src-components-Home-index__a--3EFPE" };

  可以理解為css-loader將a.css、b.css和c.css的樣式內容以字符串的形式拼接在一起,並將其作為js模塊的導出內容

// css-loader源碼(經簡化) // https://github.com/webpack-contrib/css-loader/blob/master/src/index.js
import postcss from 'postcss'; module.exports = async function (content, map, meta) { const options = this.getOptions(); // 獲取配置

  const plugins = []; // 轉譯源碼所需的postcss插件
  shouldUseModulesPlugins(options, this) && plugins.push(modulesPlugins); // 處理css-modules
  shouldUseImportPlugin(options, this) && plugins.push(importPlugin); // 處理@import語句
  shouldUseURLPlugin(options, this) && plugins.push(urlPlugin); // 處理url()語句
  shouldUseIcssPlugin(options, this) && plugins.push(icssPlugin); // 處理icss相關邏輯

  if (meta && meta.ast) { // 復用前面loader生成的CSS AST(如postcss-loader)
    content = meta.ast.root; } const result = await postcss(plugins).process(content); // 使用postcss轉譯源碼

  const importCode = getImportCode(); // 需要導入的依賴語句
  const moduleCode = getModuleCode(result); // 模塊導出內容
  const exportCode = getExportCode(); // 其他需要導出的信息,如css-modules的類名映射等

  const callback = this.async(); // 異步返回
  callback(null, `${importCode}${moduleCode}${exportCode}`); };

六、style-loader

  經過css-loader的轉譯,我們已經得到了完整的css樣式代碼,style-loader的作用就是將結果以style標簽的方式插入DOM樹中。

  直覺上似乎我們只需要像下面這樣返回一段js代碼,將css-loader返回的結果插入DOM就行:

module.exports = function (content) { return ` const style = document.createElement('style'); style.innerHTML = '${content}'; document.head.appendChild(style); `; };

  但css-loader返回的不是css樣式代碼的文本,而是一個js模塊的代碼,將這些js代碼直接放進style標里顯然是不行的。

  我們來看看style-loader的實現:

// style-loader
import loaderUtils from 'loader-utils'; module.exports = function (content) { // do nothing
}; module.exports.pitch = function (remainingRequest) { /* * 用require語句獲取css-loader返回的js模塊的導出 * 用'!!'前綴跳過配置中的loader,避免重復執行 * 用remainingRequest參數獲取loader鏈的剩余部分,在本例中是css-loader、less-loader * 用loaderUtils的stringifyRequest方法將request語句中的絕對路徑轉為相對路徑 */
  const requestPath = loaderUtils.stringifyRequest(this, '!!' + remainingRequest); // 本例中requestPath為: // '!!../node_modules/css-loader/index.js!../node_modules/less-loader/dist/cjs.js!src/styles/index.less'

  return ` const content = require(${requestPath}) const style = document.createElement('style'); style.innerHTML = content; document.head.appendChild(style); `; };

  style-loader的幾個設計思路:

1、css-loader返回的樣式只能通過其js模塊的運行時得到,故使用require語句取得

2、normal方法實際上什么都沒做,在pitch方法里中斷loader鏈的執行,再以inline方式調用了后方的loader來加載當前的less文件

3、如果將pitch中的實現放到normal方法里,就會造成loader鏈執行兩遍

4、如果requestPath中沒有'!!'前綴,就會造成loader鏈被無限循環調用

  style-loader的實現邏輯比較繞,也是一個比較經典的pitch應用,理解了它的原理,就可以是說對loader的調用鏈、執行順序和模塊化輸出等有了一個比較全面的認識,推薦細細體會。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM