5

窥探css-loader与style-loader的作用

 2 years ago
source link: https://segmentfault.com/a/1190000040918194
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

大家伙都清楚在使用webpack构建前端项目时都会使用到sass-loader、less-loader、postcss-loader、css-loader、style-loader,但这些loader在其中起到什么作用呢?本篇主要阐述css-loaderstyle-loader的作用和实现,加深对loader的理解。

css-loader

css-loader 会对 @importurl() 进行处理,就像 js 解析 import/require() 一样,默认生成一个数组存放存放处理后的样式字符串,并将其导出。

假如有三个样式文件:a.module.scssb.module.scssc.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会将 DefinePluginprocess.env.NODE_ENV 的值设置为 development。为模块和 chunk 启用有效的名,也就是将模块id替换成模块名称。
image-20211105160434858.png

image-20211105160155947.pngproduction会将 DefinePluginprocess.env.NODE_ENV 的值设置为 production
为模块和 chunk 启用确定性的混淆名称FlagDependencyUsagePlugin
FlagIncludedChunksPluginModuleConcatenationPlugin
NoEmitOnErrorsPluginTerserPlugin
image-20211105160641404.pngnone不使用任何默认优化选项

执行yarn build:dev构建,分析生成的文件

(1)a.module.scss使用@import引入了b.module.scss,被处理放入同一个模块的数组___CSS_LOADER_EXPORT___中并导出

image-20211105185844556.png

(2)b.module.scss使用@import url()引入了c.module.scss,但c.module.scss被单独处理放入另一个模块的数组___CSS_LOADER_EXPORT___中并导出

image-20211105190430662.png

(3)a.module.scssb.module.scss被处理放入一个模块数组,c.module.scss被单独处理放入另一个模块的数组,但是b.module.scssc.module.scss是由引入关系的,这个在构建后怎么关联依赖的呢?

a.module.scssb.module.scss被处理放入模块id12的数组,c.module.scss被处理放入模块id15的数组,模块id12的模块中导入了模块id15中的样式数组,并将其追加到模块id12的数组中,最后统一以数组__WEBPACK_DEFAULT_EXPORT__导出,css-loader处理到这一步就结束了

另外css-loader还提供其他的功能,比如css modules,想要了解可以参照例子开启css modules构建,窥其原理,此处不作介绍

image-20211105192051078.png

image-20211105191531780.png

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插入方式进行分析:

上面说到所有的样式都追加到模块id12的模块数组中,下面先获取模块id12的模块数组,然后生成style标签将其样式插入DOM

image-20211105200147804.png

上图中_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()得到的就是下图模块返回的update方法,该方法调用了其他很多方法将样式通过style标签插入DOM

image-20211105200723559.png

css-loader

style-loader


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK