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

Luxine. 2024-08-08 08:33:05 阅读 50

<code>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.js

import 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 文件处理主要就是 eslintbabelTerser 三个工具,所以我们要提升它们的运行速度。

我们可以开启多进程同时处理 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;

// 获取处理样式的Loaders

const 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",

};



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。