webpack 5 的优化配置(2)

584 阅读7分钟

1. clean-webpack-plugin

每次打包都清除之前的文件

1.1 安装

npm install --save-dev clean-webpack-plugin

1.2 webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "production", // 设置生产环境,有默认的优化
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "main.js",
  },
  plugins: [
+    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
};

2. copy-webpack-plugin

打包时候拷贝一些开发时用到的静态资源到打包后的生产路径下

2.1 安装

npm install --save-dev copy-webpack-plugin

2.2 webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
+ const CopyWebpackPlugin = require("copy-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "main.js",
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
+    new CopyWebpackPlugin({
+      patterns: [
+    		{ from: "public", to: "." }
+    	],
+    }),
  ],
};

3. 缩小范围

3.1 resolve.extensions

指定 extension 之后可以不用在require或是import的时候加文件扩展名,会依次尝试添加扩展名进行匹配

webapck.config.js

const path = require("path");

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  resolve: {
+    extensions: [".js", ".ts", ".tsx" ".jsx", ".json", ".css"],
  },
};

3.2 resolve.alias

webapck.config.js

const path = require("path");

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  resolve: {
    extensions: [".js", ".ts", ".tsx" ".jsx", ".json", ".css"],
+    alias: {
+      "@": path.resolve("src"),
+    },
  },
};

3.3 resolve.modules

指定一些模块的查找路径

webpack.config.js

const path = require("path");

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
    resolve: {
    extensions: [".js", ".jsx", ".json"], //指定查找文件名,不指定也可以就是编写代码时写上后缀名 这定义了引入模块是,可以省略的后缀名
    alias: { // 指定查找别名
      "@": path.resolve(__dirname, "src")
    },
+    modules: ["my_modules", node_modules"], // 指定模块查找的目录
+    mainFields: ["module", "main"], // 指定从库的 package.json 中配置的属性查找,默认是 main
+    mainFiles: ["index"] // 如果 mainFields 中的找不到,默认查找库中根目录下的 index.js 文件
  },
};

3.4 resolve.mainFields

默认情况下 package.json 文件则按照文件中 main 字段的文件名来查找文件

webpack.config.js

const path = require("path");

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  resolve: {
      // other config ....
     // mainFields: 指定从库的 package.json 中配置的属性查找,默认是 main, 可以在package.json 中指定他字段 "xxx": ".../xxx.js"
+     mainFields: ["xxx", "browser", "module", "main"], // 配置 target === "web" 或者 target === "webworker" 时 mainFields 默认值是:['browser', 'module', 'main']
+     mainFields: ["module", "main"], // target 的值为其他时,mainFields 默认值为: ["module", "main"]
  },
};

3.5 resolve.mainFiles

当目录下没有 package.json 文件时,我们说会默认使用目录下的 index.js 这个文件,其实这个也是可以配置的

webapck.config.js

{
  resovle: {
    mainFiles: ["index"]; // 如果 mainFields 中的找不到,默认查找库中根目录下的 index.js 文件
  }
}

PS: require("add");

a. 去 node_modules/add 目录中查找

b. 去 node_modules/add 目录中查找 package.json 文件

c. 找到 package.json, 就读取 main 字段对应的文件, 结束

d. 如果找不到 package.json, 就找目录下的 index.js 文件

4. module.noParse

  • module.noParse 字段,用于配置哪些模块文件的内容不需要进行解析。
  • 不需要解析依赖(即无依赖) 的第三方类库等,可以提高整体的构建速度。

4.1 webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  module: {
+    noParse: /jquery|lodash/, // 正则表达式
    // 或者使用函数
+    noParse(content) {
      return /jquery|lodash/.test(content);
    },
  },
  plugins: [new CleanWebpackPlugin()],
};

PS: 使用 noParse 进行忽略的模块文件中不能使用 import、require、define 等导入机制

5. webpack.IgnorePlugin

webpack.IgnorePlugin 用于忽略某些特定的模块,让 webpack 不把这些指定的模块打包进去

5.1 src/index.js

import moment from  'moment';
import 'moment/locale/zh-cn'
console.log(moment().format('MMMM Do YYYY, h:mm:ss a'));

5.2 webpack.config.js

const path = require("path");
+ const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  plugins: [
    new CleanWebpackPlugin(),
+    new webpack.IgnorePlugin({
+      contextRegExp: /moment$/, // 匹配引入模块路径的正则表达式
+      resourceRegExp: /^\.\/locale/, // 匹配模块的对应上下文,即所在目录名
+    }),
  ],
};

6. 打包耗时分析

6.1 安装

npm install --save-dev speed-measure-webpack-plugin

webpack.config.js

const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

+ const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
+ const smp = new SpeedMeasurePlugin();

+ module.exports = smp.wrap({
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  plugins: [
    new CleanWebpackPlugin()
  ],
});

7. 打包体积大小分析

7.1 安装

npm install --save-dev webpack-bundle-analyzer

webpack.config.js

+ const BundleAnalyzerPlugin =
  require("webpack-bundle-analyzer").BundleAnalyzerPlugin;

module.exports = {
  mode: "production",
  // other code ...
  plugins: [
    new CleanWebpackPlugin(),
+    new BundleAnalyzerPlugin(),
  ],
};

8. gzip 文件压缩

8.1 安装

npm install --save-dev compression-webpack-plugin

webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

+ const CompressioWebpackPlugin = require("compression-webpack-plugin");

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  plugins: [
      new CleanWebpackPlugin(),
+      new CompressioWebpackPlugin()
  ],
};

9. CSS 提取

9.1 安装

npm install  mini-css-extract-plugin --save-dev

9.2 webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

+ const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  module: {
    rules: [
      {
        test: /\.(scss|css)$/,
+        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin()
+    new MiniCssExtractPlugin({
      filename: "css/[name].[contenthash:8].css",
    }),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
};

10. 压缩 CSS

10.1 安装

npm install --save-dev css-minimizer-webpack-plugin

10.2 webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

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

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
+  optimization: {
+    minimizer: [new CssMinimizerPlugin()],
+  },
  module: {
    rules: [
      {
        test: /\.(scss|css)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "css/[name].[contenthash:8].css",
    }),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
};

11. 压缩 JS

11.1 安装

npm install --save-dev terser-webpack-plugin

11.2 webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

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

+ const TerserPlugin = require("terser-webpack-plugin");

const config = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  optimization: {
    minimizer: true,
    minimizer: [
        new CssMinimizerPlugin(),
+        new TerserPlugin()
    ],
  },
  module: {
    rules: [
      {
        test: /\.(scss|css)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "css/[name].[contenthash:8].css",
    }),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
};

12. 清理未使用的 css 选择器

12.1 安装

npm install --save-dev purgecss-webpack-plugin

12.2 webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

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

+ const glob = require("glob");
+ const PurgeCSSPlugin = require("purgecss-webpack-plugin");
+ const PATHS = {
+	src: path.join(__dirname, "src"),
+ };

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].[contenthash:8].js?v=[contenthash:8]",
  },
  optimization: {
    minimizer: true,
    minimizer: [new CssMinimizerPlugin(), new TerserPlugin()],
  },
  module: {
    rules: [
      {
        test: /\.(scss|css)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "css/[name].[contenthash:8].css",
    }),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
+    new PurgeCSSPlugin({
+      paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
+    }),
  ],
};

13. CDN

CDN(内容分发网络),通过把资源部署到某个地方的服务器,用户在访问时按照就近原则从离用户最近的服务器获取资源,从而加速资源的获取速度。

13.1 使用缓存

  • HTML文件不缓存,放在自己的服务器上,关闭自己服务器的缓存,静态资源的 URL 变成指向 CDN 服务器的地址。
  • 静态的JavaScript、CSS、图片等文件开启 CDN 和缓存,并且文件名带上 HASH 值。
  • 为了并行加载不阻塞,把不同的静态资源分配到不同的 CDN 服务器上。

13.2 域名限制

  • 同一时刻针对同一个域名的资源并行请求进行限制
  • 把这些静态资源分散到不同的 CDN 服务上去
  • 多个域名后会增加域名解析时间
  • 通过在 HTML HEAD 标签中 加入<link rel="dns-prefetch" href="http://img.xxx.cn">去预解析域名,以降低域名解析带来的延迟

13.3 文件指纹

  • 打包后输出的文件名和后缀
  • hash 一般是结合 CDN 缓存来使用,通过 webpack 构建之后,生成对应文件名自动带上对应的 MD5 值。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的 HTML引用的 URL 地址也会改变,触发 CDN 服务器从源服务器上拉取对应数据,进而更新客户端本地缓存。

指纹占位符

占位符名称含义
ext资源后缀名
name文件名称
path文件的相对路径
folder文件所在的文件夹
fullhashwebpack5 该了,之前是 hash,每次 webpack 构建时生成一个唯一的 hash 值
chunkhash根据 chunk 生成 hash 值,来源于同一个 chunk,则 fullhash 值就一样
contenthash根据内容生成hash值,文件内容相同hash值就相同

webpack.config.js

const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

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

const TerserPlugin = require("terser-webpack-plugin");

const glob = require("glob");
const PurgeCSSPlugin = require("purgecss-webpack-plugin");
const PATHS = {
  src: path.join(__dirname, "src"),
};

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
+    filename: "[name].[chunkhash:8].js?v=[chunkhash:8]",
  },
  optimization: {
    minimizer: true,
    minimizer: [new CssMinimizerPlugin(), new TerserPlugin()],
  },
  module: {
    rules: [
      {
        test: /\.(scss|css)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new webpack.ProgressPlugin(),
    new MiniCssExtractPlugin({
+      filename: "css/[name].[contenthash:8].css?v=[contenthash:8]",
    }),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
    new PurgeCSSPlugin({
      paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
    }),
  ],
};

14. moduleIds & chunkIds 的优化

  • module: 每一个文件可看成一个 module。
  • chunk: webpack 打包最终生成的代码块,代码块会生成文件,一个文件对应一个 chunk。
  • 在 webpack5 之前,没有从 entry 打包的 chunk 文件,都会以1、2、3...的文件命名方式输出,删除某些文件可能会导致缓存失效。
  • 在生产模式下,默认启用这些功能 chunkIds: "deterministic", moduleIds: "deterministic",此算法采用确定性的方式将短数字 ID(3 或 4 个字符)短hash值分配给 modules 和 chunks。
  • chunkId设置为deterministic, webpack5新增,则 output 中 chunkFilename 里的 [name] 会被替换成确定性短数字 ID。
  • 虽然 chunkId 不变(不管值是deterministic | natural | named),但更改 chunk 内容,chunkhash 还是会改变的。
可选值含义示例
natural按使用顺序的数字ID1
named方便调试的高可读性idsrc_main_js.js
deterministic根据模块名称生成简短的hash值375
size根据模块大小生成的数字id0

14.1 webpack.config.js

module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
  },
  context: process.cwd(),
  output: {
    path: path.resolve("dist"),
    filename: "[name].js",
  },
  optimization: {
    minimizer: true,
    minimizer: [new CssMinimizerPlugin(), new TerserPlugin()],
+    moduleIds: 'deterministic',
+    chunkIds: 'deterministic'
  },
}

src/index.js

import('./one');
import('./two');
import('./three');

会在打包生成文件 output.filename: "[name].js" 将 name 以数字 ID 做为文件的命名。

开发环境: moduleIds: "named",

生产环境:moduleIds: "deterministic",

webpack 5 基本配置(1)

webpack 5 优化配置-代码分割(3)