【webpack】如何利用style-loader、css-loader、postcss-loader等配置css环境

webpack

如此熟悉的配置肯定有看到过,但是你知道他们每个loader具体是干啥的吗。

js 复制代码
const path = require("path");

module.exports = {
  entry: "./src/index.js",
  mode: "development",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

style-loader

作用

  • 将css注入到DOM。即将css样式注入到style标签

css-loader

作用

css-loader@importurl() 进行处理

几个重要属性

  • url. 启用/禁用 url/image-set 函数进行处理
less 复制代码
background-image: url('image.png') 
background-image: url('./image.png') 
background-image: url('~module/image.png')
image-set(url('image2x.png') 1x, url('image1x.png') 2x)

...
  • import. 启用/禁用 @import 规则进行处理
less 复制代码
@import 'style.css' 
// 俩种情况可以使用前缀,
// 1、`node_modules` path (include `resolve.modules`) 
// 2、for `alias`
@import url('~module/style.css')
@import url('~module/style.css')
...

以前我就碰过这种问题,在css中为啥不可以使用 ~ ,现在总算是明白了,毕竟之前没有这样去配置过

js 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /.css$/i,
        loader: "css-loader",
        options: {
          import: true,
        },
      },
    ],
  },
};
  • importLoaders。 用于配置在处理@import规则、CSS modules 、 ICSS imports之前应用的加载器数量
js 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV === "development"
            ? "style-loader"
            : MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              importLoaders: 2,
              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
              modules: {
                localIdentName: "[local]_[hash:base64:5]",
              },
            },
          },
        ],
      },
    ],
  },
};

其余更多的属性参考css-loader

css module

现在项目里面基本上都有这种写法

css 复制代码
/* style.css */
.className {
  color: green;
}
javascript 复制代码
import styles from "./style.css";

那么webpack中应该怎么配置

js 复制代码
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV === "development"
            ? "style-loader"
            : MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[local]_[hash:base64:5]",
              },
            },
          },
        ],
      },
    ],
  },

更多参数css-loader

mini-css-extract-plugin

这个插件基于Webpack 5, 4是 extract-text-webpack-plugin

作用:将CSS提取到单独的文件中,为每个包含CSS的JS文件创建一个CSS文件,并且支持CSS和SOurceMaps的按需加载

注意: style-loader 不要和 mini-css-extract-plugin一起使用,这是因为development 环境使用了webpack-dev-server,它使用了multiple,注入css非常快。但是在production环境使用mini-css-extract-plugin分离css

js 复制代码
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          process.env.NODE_ENV === "development"
            ? "style-loader"
            : MiniCssExtractPlugin.loader,
          "css-loader",
        ],
      },
    ],
  },

预处理器

sql 复制代码
pnpm add --save-dev sass-loader less-loader stylus-loader
js 复制代码
 module: {
   rules: [
    {
      test: /\.(scss|sass)$/,
      use: [
        process.env.NODE_ENV === "development"
          ? "style-loader"
          : MiniCssExtractPlugin.loader,
        "css-loader",
        "sass-loader"
      ],
    },
  ],
},

其他也是同理

PostCSS

PostCSS是一个使用JavaScript编写的工具,用于对CSS进行转换和处理。它可以通过插件机制对CSS进行各种自定义的转换操作,从而扩展CSS的功能和语法。而其他CSS预处理器,如SassLess,主要是将特定语法的样式文件编译为浏览器可以识别的CSS文件。

两者并非是互斥的关系,既能复用预处理语法特性,又能应用PostCSS丰富的插件能力处理如浏览器前缀等问题

sql 复制代码
pnpm add postcss-loader postcss  autoprefixer --save-dev
js 复制代码
 {
   loader: "postcss-loader",
    options: {
       postcssOptions: {
          plugins: [
                [
                    "autoprefixer",
                    {
                      // autoprefixer options
                    },
             ],
          ],
      },
   },
},

完整的配置

js 复制代码
const path = require("path");
const fs = require("fs");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");

const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;

const useTailwind = fs.existsSync(
  path.resolve(__dirname, "."),
  "tailwind.config.js"
);

module.exports = function (webpackEnv) {
  const isEnvDevelopment = webpackEnv === "development";

  const getStyleLoaders = (cssOptions, preProcessor) => {
    const loaders = [
      {
        loader: isEnvDevelopment ? "style-loader" : MiniCssExtractPlugin.loader,
      },
      {
        loader: "css-loader",
        options: cssOptions,
      },
      {
        loader: "postcss-loader",
        options: {
          postcssOptions: {
            ident: "postcss",
            config: false,
            plugins: !useTailwind
              ? [
                  "postcss-flexbugs-fixes",
                  [
                    "postcss-preset-env",
                    {
                      autoprefixer: {
                        flexbox: "no-2009",
                      },
                      stage: 3,
                    },
                  ],
                ]
              : [
                  "tailwindcss",
                  "postcss-flexbugs-fixes",
                  [
                    "postcss-preset-env",
                    {
                      autoprefixer: {
                        flexbox: "no-2009",
                      },
                      stage: 3,
                    },
                  ],
                ],
          },
        },
      },
    ].filter(Boolean);

    if (preProcessor) {
      loaders.push(
        {
          loader: require.resolve("resolve-url-loader"),
        },
        {
          loader: require.resolve(preProcessor),
          options: {
            sourceMap: true,
          },
        }
      );
    }
    return loaders;
  };

  return {
    entry: "./src/index.js",
    mode: "development",
    output: {
      path: path.resolve(__dirname, "dist"),
    },
    module: {
      rules: [
        {
          test: cssRegex,
          exclude: cssModuleRegex,
          use: getStyleLoaders({
            importLoaders: 1,

            modules: {
              mode: "icss",
            },
          }),
        },
        {
          test: cssModuleRegex,
          use: getStyleLoaders({
            importLoaders: 1,
            modules: {
              mode: "local",
              localIdentName: "[local]_[hash:base64:5]",
            },
          }),
        },
        {
          test: sassRegex,
          exclude: sassModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 3,
              modules: {
                mode: "icss",
              },
            },
            "sass-loader"
          ),
        },
        {
          test: sassModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 3,
              modules: {
                mode: "local",
                localIdentName: "[local]_[hash:base64:5]",
              },
            },
            "sass-loader"
          ),
        },
        {
          test: lessRegex,
          exclude: lessModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 3,
              modules: {
                mode: "icss",
              },
            },
            "less-loader"
          ),
        },
        {
          test: lessModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 3,
              modules: {
                mode: "local",
                localIdentName: "[local]_[hash:base64:5]",
              },
            },
            "less-loader"
          ),
        },
      ],
    },
    plugins: [new MiniCssExtractPlugin(), new HTMLWebpackPlugin()],
  };
};

以上配置支持了 csscss modulelessless modulesass/scsssass/scss module, 并且根据用户是否用了taildwindcss,而动态引入PostCSS 插件tailwindcss

这里再提醒一句,style-loadermini-css-extract-plugin需要根据环境来区分,生产用mini-css-extract-plugin, 开发用style-loader。这是因为style-loader 只会将css会处理成js文件,而mini-css-extract-plugin会分离

还有就是在处理 CSS Module的时候, mode的取值有local,也有icssglobalpure,详见css loader

  • pure: 没有全局或局部模式,所有的类名都将被局部化
  • local: 在这种模式下,所有的类名默认都会被局部化,也就是说,它们会被转换为唯一的类名以避免冲突。这意味着,即使你在不同的CSS文件中定义了相同的类名,它们也不会冲突,因为css-loader会为每个类名生成一个唯一的标识符。你可以使用:global伪类来切换到全局模式,这样类名就不会被局部化
  • global: 在这种模式下,所有的类名默认都会被视为全局类名,也就是说,它们会保持原样,不会被转换为唯一的类名。这意味着,如果你在不同的CSS文件中定义了相同的类名,它们可能会冲突。你可以使用:local伪类来切换到局部模式,这样类名就会被局部化
  • icss: 是一种CSS模块化的规范,定义了一种方式在CSS中表示导入和导出
css 复制代码
/* other.css */ 
:export { color: red; } 

/* main.css */ 
:import("./other.css") { otherColor: color; }

vite

看了下vite的文档,类似于cli工具一样,将大部分已经封装在内部。

  • 比如导入.css文件会自动插入到style标签中,源码像是在这里css实现的, 然后结合html插件html
  • 对于@importurl这种语法识别, vite是通过postcss-import来实现的

  • CSS Module 处理。如果写成module.css也会被认为是一个CSS Module文件,所以用户根本不需要操心背后干了啥, 内部用postcss-modules进行处理的

  • CSS预处理器。则需要安装的对应的预处理器依赖

bash 复制代码
# .scss and .sass 
npm add -D sass
# .less 
npm add -D less 
# .styl and .stylus 
npm add -D stylus
  • CSS压缩。vite默认采用esbuild,umi默认压缩也是esbuild, vite也可以设置lightnigncss
相关推荐
mCell2 小时前
GSAP ScrollTrigger 详解
前端·javascript·动效
gnip2 小时前
Node.js 子进程:child_process
前端·javascript
excel5 小时前
为什么在 Three.js 中平面能产生“起伏效果”?
前端
excel6 小时前
Node.js 断言与测试框架示例对比
前端
天蓝色的鱼鱼7 小时前
前端开发者的组件设计之痛:为什么我的组件总是难以维护?
前端·react.js
codingandsleeping7 小时前
使用orval自动拉取swagger文档并生成ts接口
前端·javascript
石金龙8 小时前
[译] Composition in CSS
前端·css
白水清风8 小时前
微前端学习记录(qiankun、wujie、micro-app)
前端·javascript·前端工程化
Ticnix9 小时前
函数封装实现Echarts多表渲染/叠加渲染
前端·echarts
用户22152044278009 小时前
new、原型和原型链浅析
前端·javascript