【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
相关推荐
weixin_382395234 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__4 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.5 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~6 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华6 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子9 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅9 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni9 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学10 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端