webpack 配置整理 | 青训营笔记

106 阅读5分钟

这是我参与「第四届青训营」笔记创作活动的第2天

一直说要学习 webpack, 也尝试过很多次, 每次都是从 官网的例子开始看起, 但每次总是看了前面一点后, 后面的就渐渐看不下去了。 总结了下原因, 主要是学习它的目的只是去学习, 而不是去运用, 有很多知识可能就需要实际的项目经验才能更好的理解为什么要那么做, 但自己对于 vue 和 react 官方封装的配置又不了解, 配置方法也与webpack原生的不同。

这次, 由于选的大项目是前端监控系统, 用的是 react 的技术栈, 而 react 的配置就比 vue 更麻烦了好像, vue好歹还有个 vue.config.js 可以弄, 但 react 是要 eject 后再改, 但都比 webpack 的原生配置都麻烦。

所以, 最近花时间系统的学习了下 webpack 的配置并封装了 reactvue 的两个版本的配置, 此篇文章则详细介绍下

文章同样收录到我的个人博客中,欢迎大家访问

样式配置

样式配置里主要是进行 css, less, scss, sass, stylus 的配置, 用到了以下的 loader

  • style-loader
  • css-loader
  • `less-loader
  • postcss-loader
  • stylus-loader
  • sass-loader

注意: 有时候运行 webpack 时可能会报这些 loader 的错, 我自己遇到过的用了以下两个方案解决的

    1. 用一个 oneof 包裹这些 loader
    1. 安装对应的样式包, 如 sass, postcss, less

具体配置如下

// 获取处理样式的Loader
function getStyleLoader(pre) {
  return [
    MiniCssExtractPlugin.loader,
    "css-loader",
    {
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: ["postcss-preset-env"],
        },
      },
    },
    pre,
  ].filter(Boolean);
}


module.exports = {
    module: {
      rules: [
        {
          oneOf: [
            {
              test: /.css$/,
              use: getStyleLoader(),
            },
            {
              test: /.less$/,
              use: getStyleLoader("less-loader"),
            },
            {
              test: /.s[ac]ss$/,
              use: getStyleLoader("sass-loader"),
            },
            {
              test: /.styl$/,
              use: getStyleLoader("stylus-loader"),
            },     
          ],
        },
      ],
    },
}

大家看配置代码会发现, 第一个应该是用 style-loader, 这里是用 MiniCssExtractPlugin 来代替了, 这个是用来提取 Css 成单独文件

vue 中用的是 vue-style-loader

postcss 是用来处理 css 的兼容性问题, 具体用法可以看 官方文档, 它要与 browserslist 配合使用, 需要在 package.json 中添加如下代码, 内容含义如下

  • 包含最近的两个版本
  • 覆盖 99% 的浏览器
  • ‘死亡’的浏览器配置不管

最后是取这三个条件下的交集

"browserslist": [
  "last 2 version",
  "> 1%",
  "not dead"
],

资源文件配置

资源文件主要是处理 图片和其他媒体资源。

处理图片时, 我们有时候需要将 小图片 转成 base64 格式, 以减少文件数量, 这里就用到了 这里面的 dataUrlCondition 配置。

同时, 其他文件如 字体文件,音频 等不需要进行处理, 直接进行打包的, 可以用 type:asset/resouce 格式。

这里说下 这两种格式的区别:

  1. type: "asset/resource" 相当于file-loader, 将文件转化成 Webpack 能识别的资源,其他不做处理
  2. type: "asset" 相当于url-loader, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式
module.exports = {
 module:{
   rules:[
     oneOf: [
        {
          test: /.(png|jpe?g|gif|webp|svg)$/,
          type: "asset",
          parser: {
            dataUrlCondition: {
              // 小于10kb的图片转base64
              // 优点:减少请求数量  缺点:体积会更大
              maxSize: 10 * 1024, // 10kb
            },
          },
        },
        {
          // 在这里加后缀即可
          test: /.(woff|woff2?|eot|ttf|otf)$/,
          type: "asset/resource",
        },
     ]
   ]
 }
}

js配置

js配置主要是用到了 babel 进行转义, 在 rules 中加入如下代码

{
  test: /.js$/,
  // exclude: /node_modules/, // 排除node_modules中的js文件不处理
  include: path.resolve(__dirname, "../src"),
  use: [
    {
      loader: "thread-loader",
      options: {
        works: threads,
      },
    },
    {
      loader: "babel-loader",
      options: {
        cacheDirectory: true, // 开启 babel 缓存
        cacheCompression: false, // 关闭缓存文件压缩
        plugins: ["@babel/plugin-transform-runtime"],
      },
    },
  ],
},

babel 主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中

由于 vue 和 react 官方都有一套自己的 babel 配置, 所以这里我们不需要做太多深入的研究, 使用的时候直接用对应的 preset即可

新建 babel.config.js

module.exports = {
  // @babel/preset-env: 使用最新的js
  // @babel/preset-react: 编译jsx
  // @babel/preset-ts: 编译ts
  presets: [
    [
      "@babel/preset-env",
      { useBuiltIns: "usage", corejs: { version: "3", proposals: true } },
    ],
  ],
};

eslint 配置

eslint 的配置在plugins中加入如下代码

new ESLintPlugin({
  // 检查src下的文件
  context: path.resolve(__dirname, "../src"),
  exclude: "node_modules", // 默认值
  cache: true,
  cacheLocation: path.resolve(
    __dirname,
    "../node_modules/.cache/eslintcache"
  ),
  threads, // 开启多进程和进程数量
}),

同时添加 .eslintrc.js 文件

module.exports = {
  // 解析选项
  parserOptions: {
    ecmaVersion: 11, // ES语法版本,11版本
    sourceType: "module", // ES 模块化
    ecmaFeatures: {
      jsx: true, // react项目
    },
  },
  parser: "@babel/eslint-parser", // 支持最新的最终 ECMAScript 标准

  // 具体规则
  rules: {
    semi: 1,
    "array-callback-return": 1,
    "default-case": "warn",
    eqeqeq: 1,
    "no-var": "error",
    "no-undef": 0,
  },
  // 继承现有规则
  // react: react-app
  // vue: plugin:vue/essential
  // eslint: eslint:recommended
  extends: ["eslint:recommended"],
  // 我们的规则会覆盖掉继承的
  plugins: ["import"],
};

css压缩配置

css 的压缩用到了 CssMinimizerPluginMiniCssExtractPlugin 这两个插件

这里简单说下这两个插件的作用

MiniCssExtractPlugin: 提取 Css 成单独文件。 它处理的问题是解决闪屏问题, 传统的是通过加一个style标签来加载css资源, 这就会导致css解析是在DOM解析里的, 就会出现刚开始一段时间白屏,然后突然出现全部内容。 它通过生成单文件 css, 然后用 link 标签引入, 同时解析DOM和CSS树, 来解决闪屏问题。

CssMinimizerPlugin: 这个就是css压缩的了

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");


// 获取处理样式的Loader
function getStyleLoader(pre) {
  return [
    MiniCssExtractPlugin.loader,
    "css-loader",
    {
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: ["postcss-preset-env"],
        },
      },
    },
    pre,
  ].filter(Boolean);
}

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: "static/css/[name].[contenthash:10].css",
      chunkFilename: "static/css/[name].chunk.[contenthash:10].css",
    }),
  ],

  // 压缩
  optimization: {
    minimizer: [
      new CssMinimizerPlugin(),
    ],
  },
};

js压缩配置

js 压缩用的webpack 内部的 TerserWebpackPlugin

optimization: {
  minimizer: [
    new TerserWebpackPlugin({
      parallel: threads,
    }),
},

图片压缩配置

minimizer: [
  // 压缩图片
  new ImageMinimizerPlugin({
    minimizer: {
      implementation: ImageMinimizerPlugin.imageminGenerate,
      options: {
        plugins: [
          ["gifsicle", { interlaced: true }],
          ["jpegtran", { progressive: true }],
          ["optipng", { optimizationLevel: 5 }],
          [            "svgo",            {              plugins: [                "preset-default",                "prefixIds",                {                  name: "sortAttrs",                  params: {                    xmlnsOrder: "alphabetical",                  },                },              ],
            },
          ],
        ],
      },
    },
  }),
],

prefetch与preload配置

这个是用于提前加载,

  • prefetch 是在浏览器空闲时加载, 可以理解为等当前页面所有资源加载完后, 再加载接下来可能会用到的
  • preload 优先级较高, 与当前页面资源一起加载
plugins: [
  new PreloadWebpackPlugin({
    // rel: "preload",
    // as: "script",
    rel: "prefetch",
  }),
],