Skip to content

样式处理

webpack默认不认识样式文件cssscssless 等,需要通过loader来处理

1. css处理--开发

  • 下载loader
bash
pnpm add -D style-loader css-loader

style-loader:作用是将样式文件以style标签的形式插入页面

css-loader:作用是解析css

  • 配置loader
js
// webpack.dev.js`
{
  test: /\.css$/i,
  exclude: /node_modules/,
  use: ["style-loader", "css-loader"],
},

2. scss处理--开发

  • 下载loader
bash
pnpm add -D sass sass-loader
  • 配置
js
// webpack.dev.js
{
  test: /\.sc|ass$/i,
  exclude: /node_modules/,
  use: ["style-loader", "css-loader", "sass-loader"],
},

3. css、scss处理--生产环境

开发环境下的样式文件是与js混在一起的,有利于加快构建速度,但是生产环境下(打包构建)这样可不行,我们需要将样式文件单独提出来作为独立文件,这个操作,style-loader无法处理,需要额外的loader处理

css提取与压缩

  • 安装
bash
pnpm add -D mini-css-extract-plugin
  • 配置
js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [new MiniCssExtractPlugin(
    {}
)],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"], // MiniCssExtractPlugin.loader用于提取css文件
      },
      {
        test: /\.sc|ass$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"], // MiniCssExtractPlugin.loader用于提取css文件
      },
    ],
  },
};

4. CSS Module处理

配置css-loader兼容cssModule

js
{
  test: /\.css$/i,
  exclude: /node_modules/,
  use: [
    "style-loader",
    {
      loader: "css-loader", // 解析 CSS 文件
      options: {
        modules: { namedExport: false },
      },
    },
  ],
},

就可以通过import的方式引入css文件,然后像js对象一样使用这个样式文件

存在的问题

  1. css-loader的配置导致所有css文件都开启module,会浪费编译性能

  2. 使用css-module后,css类名是一串乱码hash,不太友好

  3. 不会为module.css当都生成一个文本

解决

将css.module文件的处理的loader独立开来,不与css混在一起,并且在css-loader中排除 .module.css文件

js
rules: [
      {
        test: /\.css$/i,    // 仅处理正常的css
        exclude: [/node_modules/, /\.module\.css$/i],
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.module\.css$/i, // 专门处理 cssModule
        exclude: [/node_modules/],

        use: [
          "style-loader",
          {
            loader: "css-loader", // 解析 CSS 文件
            options: {
              modules: { 
                namedExport: false, // 是否启用命名导出
                localIdentName: "[name]__[local]--[hash:base64:5]", // 生成的类名格式
               },
            },
          },
        ],
      }
    ],

5. CSS后处理器

作用:兼容性处理、添加浏览器前缀

bash
pnpm add -D postcss poscss-loader postcss-preset-env

postcss-preset-env:一堆预设,包含了autofix等很多功能

PostCSS 插件传送门

  • 配置

css-loader后面加上postcss-loader

js
{
  test: /\.sc|ass$/i,
  exclude: /node_modules/,
  use: [
    "style-loader",
    {
      loader: "css-loader", // 解析 CSS 文件
      options: {
        modules: { namedExport: false },
      },
    },
    "postcss-loader", // 处理 CSS 前缀
    "sass-loader", // 解析 Sass 文件
  ],
},

创建postcss.config.js文件

js
// postcss.config.js
module.exports = {
  plugins: [["postcss-preset-env"]],
};

6. 自动注入全局样式文件

场景:项目中有一个全局的scss变量文件variables.scss,每个每个scss或者vue sfc文件都需要通过@import '@/assets/styles/variables.scss';来使用

就比较麻烦, 此时style-resources-loader就派上用场了[style-resources-loader](样式资源加载器 - npm)传送门

  • 安装
bash
pnpm add -D style-resources-loader
  • 配置
js
const path = require('path');
module.exports = {
    // ...
    module: {
        rules: [{
            test: /\.scss$/,
            use: ['style-loader', 'css-loader', 'sass-loader', {
                loader: 'style-resources-loader',
                options: {
                    patterns: [
                        path.resolve( __dirname, "../src/assets/styles/variables.scss" ),
                    ]
                }
            }]
        }]
    },
    // ...
}

7. 样式处理优化

优化点1:将样式处理提取到webpack.base.js中,通过打包环境区分是否要提取css

优化点2:使用oneof优化css处理

优化点3:将处理css的loader封装成函数

最终结果

  • 获取loader辅助函数
js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const getStyleLoader = () => {
    // 需要先设置好环境变量
  if (process.env.NODE_ENV === "production") {
    return MiniCssExtractPlugin.loader;
  }
  return "style-loader";
};

const getCssLoaders = (isModule) => {
  return !isModule
    ? "css-loader"
    : {
        loader: "css-loader", // 解析 CSS 文件
        options: {
          modules: {
            namedExport: false, // 是否启用命名导出
            localIdentName: "[name]__[local]--[hash:base64:5]", // 生成的类名格式
          },
        },
      };
};

module.exports = {
  getStyleLoader,
  getCssLoaders,
};
  • webpack.base.js
js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const VueLoaderPlugin = require("vue-loader").VueLoaderPlugin;
const webpack = require("webpack");
const { getStyleLoader, getCssLoaders } = require("./util");

/**
 * @type {import('webpack').Configuration}
 * @description webpack基础配置
 */
module.exports = {
  entry: path.resolve(__dirname, "../src/main.ts"), // 入口文件
  output: {
    path: path.resolve(__dirname, "../dist"), // 打包后输出目录
    filename: "js/[name].[contenthash:6].js",
    clean: true, // 清空上一次打包的输出目录, webpack5内置
    publicPath: "/", // 打包后的资源路径前缀
  },
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "../src"), // @指向src目录
      vue$: "vue/dist/vue.runtime.esm-bundler.js", // vue的运行时模块别名
    },
    extensions: [".ts", ".js", ".vue", ".json"], // 解析文件后缀名
    modules: [path.resolve(__dirname, "../src"), "node_modules"], // 告诉 webpack 解析模块时应该搜索的目录。
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"), // 模板文件
      filename: "index.html", // 打包后的文件名
      title: "webpack5-vue3-ts", // 页面标题
      inject: true, // 是否自动注入js和css文件
    }),
    new VueLoaderPlugin(),
    new webpack.DefinePlugin({
      __VUE_OPTIONS_API__: true, // 开启options api 解除控制台警告
      __VUE_PROD_DEVTOOLS__: false, // 生产环境不启用devtools 解除控制台警告
      __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false, // 禁用生产中水合不匹配的详细警告
    }),
  ],
  module: {
    rules: [
      {
        test: /\.m?jsx?$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },
      {
        test: /\.vue$/,
        loader: "vue-loader",
      },
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "ts-loader",
            options: {
              transpileOnly: true, // 只进行类型检查,不进行编译
              appendTsSuffixTo: [/\.vue$/], // 给.vue文件添加ts后缀
            },
          },
          "babel-loader",
        ],
      },
      {
        oneOf: [
          {
            test: /\.css$/i,
            exclude: [/node_modules/, /\.module\.css$/i],
            use: [getStyleLoader(), getCssLoaders(), "postcss-loader"],
          },
          {
            test: /\.module\.css$/i,
            exclude: [/node_modules/],
            use: [
              getStyleLoader(),
              getCssLoaders(true),
              "postcss-loader", // 处理 CSS 前缀
            ],
          },
          {
            test: /\.sc|ass$/i,
            exclude: /node_modules/,
            use: [
              getStyleLoader(),
              getCssLoaders(),
              "postcss-loader", // 处理 CSS 前缀
              "sass-loader", // 解析 Sass 文件
              {
                loader: "style-resources-loader",
                options: {
                  patterns: [
                    path.resolve( __dirname, "../src/assets/styles/variables.scss" ),
                  ], // 引入全局变量和混入
                },
              },
            ],
          },
        ],
      },
    ],
  },
};

然后,删除webpack.dev.js webpack.prod.js里面的样式处理loader即可