小工具知识网

当前位置: 首页 > 行业知识 > 【前端webpack5高级优化】提升打包构建速度几种优化方案

行业知识

【前端webpack5高级优化】提升打包构建速度几种优化方案

2026-11-15 09:59:48 来源:互联网转载

HotModuleReplacement(HMR/热模块替换)

开发时我们修改了其中一个模块代码,Webpack 默认会将所有模块全部重新打包编译,速度很慢

所以我们需要做到修改某个模块代码,就只有这个模块代码需要重新打包编译,其他模块不变,这样打包速度就能很快

使用HotModuleReplacement(HMR/热模块替换):在程序运行中,替换、添加或删除模块,而无需重新加载整个页面

使用流程

1.基本配置

module.exports = {  // 其他省略  devServer: {    host: "localhost", // 启动服务器域名    port: "3000", // 启动服务器端口号    open: true, // 是否自动打开浏览器    hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了)  },};

此时 css 样式经过 style-loader 处理,已经具备 HMR 功能了。 但是 js 还不行

2.js配置

// main.jsimport count from "./js/count";import sum from "./js/sum";// 引入资源,Webpack才会对其打包import "./css/iconfont.css";import "./css/index.css";import "./less/index.less";import "./sass/index.sass";import "./sass/index.scss";import "./styl/index.styl";const result1 = count(2, 1);console.log(result1);const result2 = sum(1, 2, 3, 4);console.log(result2);// 判断是否支持HMR功能if (module.hot) {  module.hot.accept("./js/count.js", function (count) {    const result1 = count(2, 1);    console.log(result1);  });  module.hot.accept("./js/sum.js", function (sum) {    const result2 = sum(1, 2, 3, 4);    console.log(result2);  });}

上面这样写会很麻烦,所以实际开发我们会使用其他loader来解决。

比如:vue-loader, react-hot-loader


OneOf

打包时每个文件都会经过所有loader处理,虽然因为 test 正则原因实际没有处理上,但是都要过一遍,比较慢

使用OneOf:顾名思义就是只能匹配上一个 loader, 剩下的就不匹配了

使用流程

const path = require("path");const ESLintWebpackPlugin = require("eslint-webpack-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {  entry: "./src/main.js",  output: {    path: undefined, // 开发模式没有输出,不需要指定输出目录    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中    // clean: true, // 开发模式没有输出,不需要清空输出结果  },  module: {    rules: [      {        oneOf: [          {            // 用来匹配 .css 结尾的文件            test: /\.css$/,            // use 数组里面 Loader 执行顺序是从右到左            use: ["style-loader", "css-loader"],          },          {            test: /\.less$/,            use: ["style-loader", "css-loader", "less-loader"],          },          {            test: /\.s[ac]ss$/,            use: ["style-loader", "css-loader", "sass-loader"],          },          {            test: /\.styl$/,            use: ["style-loader", "css-loader", "stylus-loader"],          },          {            test: /\.(png|jpe?g|gif|webp)$/,            type: "asset",            parser: {              dataUrlCondition: {                maxSize: 10 * 1024, // 小于10kb的图片会被base64处理              },            },            generator: {              // 将图片文件输出到 static/imgs 目录中              // 将图片文件命名 [hash:8][ext][query]              // [hash:8]: hash值取8位              // [ext]: 使用之前的文件扩展名              // [query]: 添加之前的query参数              filename: "static/imgs/[hash:8][ext][query]",            },          },          {            test: /\.(ttf|woff2?)$/,            type: "asset/resource",            generator: {              filename: "static/media/[hash:8][ext][query]",            },          },          {            test: /\.js$/,            exclude: /node_modules/, // 排除node_modules代码不编译            loader: "babel-loader",          },        ],      },    ],  },  plugins: [    new ESLintWebpackPlugin({      // 指定检查文件的根目录      context: path.resolve(__dirname, "../src"),    }),    new HtmlWebpackPlugin({      // 以 public/index.html 为模板创建文件      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源      template: path.resolve(__dirname, "../public/index.html"),    }),  ],  // 开发服务器  devServer: {    host: "localhost", // 启动服务器域名    port: "3000", // 启动服务器端口号    open: true, // 是否自动打开浏览器    hot: true, // 开启HMR功能  },  mode: "development",  devtool: "cheap-module-source-map",};

Include/Exclude

开发时我们需要使用第三方的库或插件,所有文件都下载到 node_modules 中了。而这些文件是不需要编译可以直接使用的。

所以我们在对js文件处理时,要排除 node_modules 下面的文件

使用Include/Exclude:

include

包含,只处理 xxx 文件

exclude

排除,除了 xxx 文件以外其他文件都处理

使用流程

const path = require("path");const ESLintWebpackPlugin = require("eslint-webpack-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {  entry: "./src/main.js",  output: {    path: undefined, // 开发模式没有输出,不需要指定输出目录    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中    // clean: true, // 开发模式没有输出,不需要清空输出结果  },  module: {    rules: [      {        oneOf: [          {            // 用来匹配 .css 结尾的文件            test: /\.css$/,            // use 数组里面 Loader 执行顺序是从右到左            use: ["style-loader", "css-loader"],          },          {            test: /\.less$/,            use: ["style-loader", "css-loader", "less-loader"],          },          {            test: /\.s[ac]ss$/,            use: ["style-loader", "css-loader", "sass-loader"],          },          {            test: /\.styl$/,            use: ["style-loader", "css-loader", "stylus-loader"],          },          {            test: /\.(png|jpe?g|gif|webp)$/,            type: "asset",            parser: {              dataUrlCondition: {                maxSize: 10 * 1024, // 小于10kb的图片会被base64处理              },            },            generator: {              // 将图片文件输出到 static/imgs 目录中              // 将图片文件命名 [hash:8][ext][query]              // [hash:8]: hash值取8位              // [ext]: 使用之前的文件扩展名              // [query]: 添加之前的query参数              filename: "static/imgs/[hash:8][ext][query]",            },          },          {            test: /\.(ttf|woff2?)$/,            type: "asset/resource",            generator: {              filename: "static/media/[hash:8][ext][query]",            },          },          {            test: /\.js$/,            // exclude: /node_modules/, // 排除node_modules代码不编译            include: path.resolve(__dirname, "../src"), // 也可以用包含            loader: "babel-loader",          },        ],      },    ],  },  plugins: [    new ESLintWebpackPlugin({      // 指定检查文件的根目录      context: path.resolve(__dirname, "../src"),      exclude: "node_modules", // 默认值    }),    new HtmlWebpackPlugin({      // 以 public/index.html 为模板创建文件      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源      template: path.resolve(__dirname, "../public/index.html"),    }),  ],  // 开发服务器  devServer: {    host: "localhost", // 启动服务器域名    port: "3000", // 启动服务器端口号    open: true, // 是否自动打开浏览器    hot: true, // 开启HMR功能  },  mode: "development",  devtool: "cheap-module-source-map",};

Cache

每次打包时 js文件都要经过Eslint检查 和Babel编译,速度比较慢。

我们可以缓存之前的 Eslint 检查 和Babel编译结果,这样第二次打包时速度就会更快了

使用Cache : 对Eslint检查 和Babel编译结果进行缓存

使用流程

const path = require("path");const ESLintWebpackPlugin = require("eslint-webpack-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {  entry: "./src/main.js",  output: {    path: undefined, // 开发模式没有输出,不需要指定输出目录    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中    // clean: true, // 开发模式没有输出,不需要清空输出结果  },  module: {    rules: [      {        oneOf: [          {            // 用来匹配 .css 结尾的文件            test: /\.css$/,            // use 数组里面 Loader 执行顺序是从右到左            use: ["style-loader", "css-loader"],          },          {            test: /\.less$/,            use: ["style-loader", "css-loader", "less-loader"],          },          {            test: /\.s[ac]ss$/,            use: ["style-loader", "css-loader", "sass-loader"],          },          {            test: /\.styl$/,            use: ["style-loader", "css-loader", "stylus-loader"],          },          {            test: /\.(png|jpe?g|gif|webp)$/,            type: "asset",            parser: {              dataUrlCondition: {                maxSize: 10 * 1024, // 小于10kb的图片会被base64处理              },            },            generator: {              // 将图片文件输出到 static/imgs 目录中              // 将图片文件命名 [hash:8][ext][query]              // [hash:8]: hash值取8位              // [ext]: 使用之前的文件扩展名              // [query]: 添加之前的query参数              filename: "static/imgs/[hash:8][ext][query]",            },          },          {            test: /\.(ttf|woff2?)$/,            type: "asset/resource",            generator: {              filename: "static/media/[hash:8][ext][query]",            },          },          {            test: /\.js$/,            // exclude: /node_modules/, // 排除node_modules代码不编译            include: path.resolve(__dirname, "../src"), // 也可以用包含            loader: "babel-loader",            options: {              cacheDirectory: true, // 开启babel编译缓存              cacheCompression: false, // 缓存文件不要压缩            },          },        ],      },    ],  },  plugins: [    new ESLintWebpackPlugin({      // 指定检查文件的根目录      context: path.resolve(__dirname, "../src"),      exclude: "node_modules", // 默认值      cache: true, // 开启缓存      // 缓存目录      cacheLocation: path.resolve(        __dirname,        "../node_modules/.cache/.eslintcache"      ),    }),    new HtmlWebpackPlugin({      // 以 public/index.html 为模板创建文件      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源      template: path.resolve(__dirname, "../public/index.html"),    }),  ],  // 开发服务器  devServer: {    host: "localhost", // 启动服务器域名    port: "3000", // 启动服务器端口号    open: true, // 是否自动打开浏览器    hot: true, // 开启HMR功能  },  mode: "development",  devtool: "cheap-module-source-map",};

Thead

当项目越来越庞大时,打包速度越来越慢,甚至于需要一个下午才能打包出来代码。这个速度是比较慢的。

我们想要继续提升打包速度,其实就是要提升js的打包速度,因为其他文件都比较少。

而对 js 文件处理主要就是 eslint 、babel、Terser 三个工具,所以我们要提升它们的运行速度。

我们可以开启多进程同时处理 js 文件,这样速度就比之前的单进程打包更快了

使用Thead 多进程打包:开启电脑的多个进程同时干一件事,速度更快。

需要注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为600ms左右开销

使用流程

我们启动进程的数量就是我们 CPU 的核数

1.如何获取 CPU 的核数,因为每个电脑都不一样

// nodejs核心模块,直接使用const os = require("os");// cpu核数const threads = os.cpus().length;

2.下载包

npm i thread-loader -D

3.使用

const os = require("os");const path = require("path");const ESLintWebpackPlugin = require("eslint-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");// cpu核数const threads = os.cpus().length;// 获取处理样式的Loadersconst getStyleLoaders = (preProcessor) => {  return [    MiniCssExtractPlugin.loader,    "css-loader",    {      loader: "postcss-loader",      options: {        postcssOptions: {          plugins: [            "postcss-preset-env", // 能解决大多数样式兼容性问题          ],        },      },    },    preProcessor,  ].filter(Boolean);};module.exports = {  entry: "./src/main.js",  output: {    path: path.resolve(__dirname, "../dist"), // 生产模式需要输出    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中    clean: true,  },  module: {    rules: [      {        oneOf: [          {            // 用来匹配 .css 结尾的文件            test: /\.css$/,            // use 数组里面 Loader 执行顺序是从右到左            use: getStyleLoaders(),          },          {            test: /\.less$/,            use: getStyleLoaders("less-loader"),          },          {            test: /\.s[ac]ss$/,            use: getStyleLoaders("sass-loader"),          },          {            test: /\.styl$/,            use: getStyleLoaders("stylus-loader"),          },          {            test: /\.(png|jpe?g|gif|webp)$/,            type: "asset",            parser: {              dataUrlCondition: {                maxSize: 10 * 1024, // 小于10kb的图片会被base64处理              },            },            generator: {              // 将图片文件输出到 static/imgs 目录中              // 将图片文件命名 [hash:8][ext][query]              // [hash:8]: hash值取8位              // [ext]: 使用之前的文件扩展名              // [query]: 添加之前的query参数              filename: "static/imgs/[hash:8][ext][query]",            },          },          {            test: /\.(ttf|woff2?)$/,            type: "asset/resource",            generator: {              filename: "static/media/[hash:8][ext][query]",            },          },          {            test: /\.js$/,            // exclude: /node_modules/, // 排除node_modules代码不编译            include: path.resolve(__dirname, "../src"), // 也可以用包含            use: [              {                loader: "thread-loader", // 开启多进程                options: {                  workers: threads, // 数量                },              },              {                loader: "babel-loader",                options: {                  cacheDirectory: true, // 开启babel编译缓存                },              },            ],          },        ],      },    ],  },  plugins: [    new ESLintWebpackPlugin({      // 指定检查文件的根目录      context: path.resolve(__dirname, "../src"),      exclude: "node_modules", // 默认值      cache: true, // 开启缓存      // 缓存目录      cacheLocation: path.resolve(        __dirname,        "../node_modules/.cache/.eslintcache"      ),      threads, // 开启多进程    }),    new HtmlWebpackPlugin({      // 以 public/index.html 为模板创建文件      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源      template: path.resolve(__dirname, "../public/index.html"),    }),    // 提取css成单独文件    new MiniCssExtractPlugin({      // 定义输出文件名和目录      filename: "static/css/main.css",    }),    // css压缩    // new CssMinimizerPlugin(),  ],  optimization: {    minimize: true,    minimizer: [      // css压缩也可以写到optimization.minimizer里面,效果一样的      new CssMinimizerPlugin(),      // 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了      new TerserPlugin({        parallel: threads // 开启多进程      })    ],  },  // devServer: {  //   host: "localhost", // 启动服务器域名  //   port: "3000", // 启动服务器端口号  //   open: true, // 是否自动打开浏览器  // },  mode: "production",  devtool: "source-map",};

上一篇:服务器防护评测怎么做?

下一篇:揭秘垂直线奥秘,背后的秘密大揭秘!