2. webpack 对样式的处理

158 阅读4分钟

为什么要打包样式

component.js

import "../css/index.css";
function component() {
  const element = document.createElement("div");
  element.innerHTML = "Hello World";
  element.className = "content";
  return element;
}
document.body.appendChild(component());

/css/index.css

.content{
    color: red;
}

执行 npm run build 之后
webpack 可以处理 js 文件,但是当代码中 import 引入了 css 文件,webpack 不知道如何对其进行加载QQ截图20231028163748.png

css-loader

css-loader 用于 css 文件的加载,将CSS资源编译成commonJS模块放到JS中。
安装:

npm install css-loader -D

css-loader 的使用

module.exports = {
  entry: "./src/main.js",
  output: {
    filename: "bundle.js",
    // 必须是一个绝对路径
    path: path.resolve(__dirname, "./build")
  },
  module: {
    rules: [
      {
        test: /\.css$/, // 匹配资源
        use: [
          { loader: "css-loader" }
        ]
      }
    ]
  }
}

打包之后可以看到 css 代码已经加载到 js 中了
QQ截图20240330171859.png QQ截图20240330172825.png

css-loader 只是负责将 .css 文件进行加载,并不会将加载之后的 css 插入到页面中,需要用 style-loader 完成插入样式的操作。

style-loader

将JS中的CSS通过创建style标签添加到HTML文件中生效。
安装:

npm install style-loader -D

配置:

      {
        test: /\.css$/, // 匹配资源
        use: [
          { loader: "style-loader" },
          { loader: "css-loader" }
        ]
      }

打包之后
QQ截图20240330172548.png QQ截图20240330172733.png

less

1. less 处理
.less 文件可以使用 less 工具来完成它的编译,

  • 安装:npm install less -D
  • 执行:npx less ./src/css/title.less > title.css less-loader 可以识别 less 文件,加载 less 文件
    index.less
@fontSize: 50px;
@fontWeight: 700;

.content {
  font-size: @fontSize;
  font-weight: @fontWeight;
}

执行:npx less ./src/css/title.less > title.css

.content {
  font-size: 50px;
  font-weight: 700;
}

2. less-loader 处理

  • 作用:将less处理成css
  • 安装: npm install less less-loader -D (less-loader下载会自动把less也下载了)
  • 配置 webpack.config.js
      {
        test: /\.less$/,
        use: [
          "style-loader",
          "css-loader",
          "less-loader"
        ]
      }

Sass 和 Scss 资源

下载包

npm i sass-loader sass -D
  • sass-loader:负责将 Sass 文件编译成 css 文件
  • sasssass-loader 依赖 sass 进行编译 配置方式:
      {
        test: /.s[ac]ss$/,
        use: ["style-loader", "css-loader", "sass-loader"],
      }

浏览器兼容性

兼容性是针对不同的浏览器支持的特性:比如css特性、js语法,之间的兼容性。在很多的脚手架配置中,都能看到类似于这样的配置信息

> 1% 
last 2 versions
not dead

查询到浏览器的市场占有率:"Can I use" usage table

.browserslist

不同的浏览器对于样式文件的支持度是不一样的,有的样式在部分浏览器中使用可能要加前缀,需要支持哪些浏览器的兼容性,这个是可以自己指定的。指定好支持的规则之后,用 browserslist 工具去查询
browserslist 工具
Browserslist是一个 在不同的前端工具之间 ,共享 目标浏览器和Node.js版本的配置

  • Autoprefixer
  • Babel
  • postcss-preset-env
  • eslint-plugin-compat
  • stylelint-no-unsupported-browser-features
  • postcss-normalize
  • obsolete-webpack-plugin

浏览器查询过程

  1. 可以编写类似于这样的配置:
> 1% 
last 2 versions 
not dead

49e171f690f54b338e29a1b51d0c1d56~tplv-k3u1fbpfcp-jj-mark_3024_0_0_0_q75.webp 2. caniuse-lite 的工具进行查询。(来自于 caniuse 的网站) QQ截图20231028172722.png 如果没写 npx browserslist 后面的执行条件,会默认获取 .browserslistrc 中的查询条件 项目中的编写
工程化项目中创建 .browserslistrc 文件,打包的时候就会看这个文件中的条件。

PostCSS

PostCSS 是一个通过 JavaScript 来转换样式的工具,如:自动添加浏览器前缀、css样式的重置。需要借助 postcss 对应的插件。
PostCSS 实际的作用:

  1. 解析css代码,解析成抽象语法树。
  2. 将抽象语法树交给插件处理。
  3. 将插件处理之后的抽象语法树重新文本化。

1. 命令行使用postcss
安装:npm install postcss postcss-cli -D
案例:写一个添加前缀的 css
安装 autoprefixer npm install autoprefixer -D
使用postcss工具,并且指定使用autoprefixer:npx postcss --use autoprefixer -o end.css ./src/css/style.css
转换之前:

.content{
  user-select: none;
}

转换之后:

.content{
  -webkit-user-select: none;
     -moz-user-select: none;
          user-select: none;
}

2. 工程化项目中使用 postcss
安装: npm install postcss-loader -D
在使用 css-loader 之前使用 postcss-loader,使用 postcss-loader 的目的是在项目中能使用 postcss

const path = require('path');

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    // 必须是一个绝对路径
    path: path.resolve(__dirname, "./build")
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        use: [
          "style-loader",
          "css-loader",
          {
            loader:"postcss-loader",
            options:{
              postcssOptions:{
                plugins:[
                  require('autoprefixer')
                ]
              }
            }
          },
          "less-loader"
        ]
      }
    ]
  }
}

importLoaders

为什么要有 importLoaders ?

  • index.css(index.css 中引入了 test.css)
@import './test.css'
  • test.css
.content{
  user-select: none;
}
  • webpack
const path = require('path');
module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    // 必须是一个绝对路径
    path: path.resolve(__dirname, "./build")
  },
  module: {
    rules: [
      {
        test: /\.css$/, // 匹配资源
        use: [
          { loader: "style-loader" },
          { loader: "css-loader" },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  require('autoprefixer')
                ]
              }
            }
          }
        ]
      }
    ]
  }
}

打包之后,发现没有将 css 代码加前缀。

.content{
    user-select: none; 
}

原因是:webpack.config.js 中的配置,匹配到 css 文件的时候用 postcss-loader 处理 index.css ,然后用 css-loader 处理 index.css,css-loader 是可以处理 @import 语法,引入了 test.css,处理 test.css。但是 postcss-loader 没有处理 test.css 中的内容。 ["style-loader","css-loader","postcss-loader"] 是不会逆序再执行的。

解决方式:需要让 postcss-loader 处理 test.css

const path = require('path');
module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    // 必须是一个绝对路径
    path: path.resolve(__dirname, "./build")
  },
  module: {
    rules: [
      {
        test: /\.css$/, // 匹配资源
        use: [
          { loader: "style-loader" },
          {
            loader: "css-loader",
            options: {
              importLoaders: 1
            }
          },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  require('autoprefixer')
                ]
              }
            }
          }
        ]
      }
    ]
  }
}

打包之后

.content{
    -webkit-user-select: none; 
    -moz-user-select: none; 
    user-select: none; 
}

提取 Css 成单独文件

Css 文件目前被打包到 js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式,这样对于网站来说,会出现闪屏现象,用户体验不好。

npm i mini-css-extract-plugin -D
  • 配置
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
      },
      {
        test: /\.s[ac]ss$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
      {
        test: /\.styl$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "stylus-loader"],
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
    }),
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "static/css/main.css",
    }),
  ],
  mode: "production",
};

postcss 预设

上面的案例只处理了 箭头函数 ,怎么能处理更多的css语法呢?

npm i postcss-loader postcss postcss-preset-env -D
  • 配置
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
    clean: true,
  },
  module: {
    rules: [
      {
        // 用来匹配 .css 结尾的文件
        test: /\.css$/,
        // use 数组里面 Loader 执行顺序是从右到左
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  "postcss-preset-env", // 能解决大多数样式兼容性问题
                ],
              },
            },
          },
        ],
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  "postcss-preset-env", // 能解决大多数样式兼容性问题
                ],
              },
            },
          },
          "less-loader",
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
    }),
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "static/css/main.css",
    }),
  ],
  mode: "production",
};
  • 控制兼容性: 可以在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度。
{
  // 其他省略
  "browserslist": ["ie >= 8"]
}

Css 压缩

npm i css-minimizer-webpack-plugin -D
  • 配置
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
    clean: true,
  },
  module: {
    rules: [
      {
        // 用来匹配 .css 结尾的文件
        test: /\.css$/,
        // use 数组里面 Loader 执行顺序是从右到左
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  "postcss-preset-env", // 能解决大多数样式兼容性问题
                ],
              },
            },
          },
        ],
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  "postcss-preset-env", // 能解决大多数样式兼容性问题
                ],
              },
            },
          },
          "less-loader",
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
    }),
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "static/css/main.css",
    }),
    // css压缩
    new CssMinimizerPlugin(),
  ],
  mode: "production",
};

附加
指定 webpack 打包入口
默认会从 src/index.js 开始打包。但是也可以修改入口文件npx webpack --entry ./src/main.js
指定 webpack 配置文件路径

"script":{
    "build": "webpack --config ./wk.config.js"
}