webpack优化
admin
2024-04-11 15:27:34
0

提升开发体验

SourceMap

为什么

开发时我们运行的代码是经过 webpack 编译后的,例如下面这个样子:

/** ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").* This devtool is neither made for production nor for readable output files.* It uses "eval()" calls to create a separate source file in the browser devtools.* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)* or disable the default devtool with "devtool: false".* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).*/
/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({/***/ "./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/less/index.less":
/*!**********************************************************************************************************!*\!*** ./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/less/index.less ***!\**********************************************************************************************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \".box2 {\\n  width: 100px;\\n  height: 100px;\\n  background-color: deeppink;\\n}\\n\", \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack://webpack5/./src/less/index.less?./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js");/***/ }),

// 其他省略
所有 css 和 js 合并成了一个文件,并且多了其他代码。此时如果代码运行出错那么提示代码错误位置我们是看不懂的。一旦将来开发代码文件很多,那么很难去发现错误出现在哪里。

所以我们需要更加准确的错误提示,来帮助我们更好的开发代码。

是什么

SourceMap(源代码映射)是一个用来生成源代码与构建后代码一一映射的文件的方案。

它会生成一个 xxx.map 文件,里面包含源代码和构建后代码每一行、每一列的映射关系。当构建后代码出错了,会通过 xxx.map 文件,从构建后代码出错位置找到映射后源代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快的找到错误根源。

怎么用

通过查看Webpack DevTool 文档可知,SourceMap 的值有很多种情况.

但实际开发时我们只需要关注两种情况即可:

开发模式:cheap-module-source-map

优点:打包编译速度快,只包含行映射
缺点:没有列映射

module.exports = {// 其他省略mode: "development",devtool: "cheap-module-source-map",
};

生产模式:source-map
优点:包含行/列映射
缺点:打包编译速度更慢

module.exports = {// 其他省略mode: "production",devtool: "source-map",
};

提升打包构建速度

HotModuleReplacement

为什么

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

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

是什么

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

怎么用

基本配置

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

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

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 正则原因实际没有处理上,但是都要过一遍。比较慢。

是什么

顾名思义就是只能匹配上一个 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
包含,只处理 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 编译结果,这样第二次打包时速度就会更快了。

是什么

对 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 文件,这样速度就比之前的单进程打包更快了。

是什么

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

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

怎么用

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

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

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

npm i thread-loader -D
使用

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",
};

我们目前打包的内容都很少,所以因为启动进程开销原因,使用多进程打包实际上会显著的让我们打包时间变得很长。

相关内容

热门资讯

成分股集体上涨,港股通汽车ET... 截至2月24日14点10分,上证指数涨0.89%,深证成指涨1.41%,创业板指涨1.11%。ETF...
韩国股指再刷新历史新高!2月最... 来源:财联社 财联社2月23日讯(编辑 马兰)周一,受益于美国对等关税政策无效、人工智能热潮继续刺激...
影石开工红包:每人100,到岗... 2月24日消息,影石为员工安排开工红包,每人100,当天到岗就有。 据介绍,该红包由部门leader...
太疯狂!“现在至少要10万”,... 2月24日早盘,贵金属盘初直线飙升。现货黄金突破5240美元/盎司,触及三周高点,纽约期金突破526...
别盲目跟风!华工科技赶单忙到疯... 最近华工科技涨得有点猛,身边不少人问我是不是又在炒概念。其实吧,这次真不是瞎炒,背后有实打实的订单撑...
韩广岳再辞任董事长职务,李学军... 近日,中邮资管发布公告称,因年龄原因,韩广岳正式向董事会提交辞去公司董事长、董事会投资决策委员会主任...
2026年中国智能舒缓穿戴设备... 华经产业研究院为助力企业、科研、投资机构等单位了解智能舒缓穿戴设备行业发展态势及未来趋势,特重磅推出...
茅台带动春节白酒价格触底止跌,... 虽然业内对2026年春节白酒市场有着“最冷”的担忧,但记者了解到,在茅台的带动下,尽管白酒市场整体需...
广东省情中心:广东生产性服务业... 2月24日,春节后首个工作日,广东连续第四年召开全省高质量发展大会,今年聚焦“制造业与服务业协同发展...
2022三文鱼行业及市场洞察报... 今天分享的是:2022三文鱼行业及市场洞察报告 报告共计:32页 三文鱼作为一种备受青睐的高端水产,...
AI芯片新突破 12只概念股获... 来源:证券时报e公司 人民财讯2月24日电,据科技日报2月23日报道,北京大学电子学院研究员邱晨光团...
原创 不... 特朗普突然改口,全球关税在短时间内再度上调5%,引发市场与舆论的广泛关注。有分析指出,在这一轮博弈中...
湖南三线小城春节消费火爆,商场... 常德街道。胡雅文/摄 本报(chinatimes.net.cn)记者胡雅文 常德报道 马年春节,常德...
小红书负面笔记处理|品牌舆情优... 在当下的社交媒体版图中,小红书早已超越单纯的“种草社区”定位,成长为月活跃用户突破3亿、每日新增笔记...
原创 俄... 最近,国际金融圈发生了一件让许多观察者都感到摸不着头脑的大事——俄罗斯正在源源不断地将黄金运往东方,...
知名食品上市公司实控人陈飞龙去... 2月23日晚间,南侨食品(SH605339)公告称,公司董事会收到公司实控人之一陈飞龙家属通知,陈飞...
高开高走!马年首日三大股指半日... 金融投资报记者 林珂 马年首个交易日大盘如期高开高走,早盘三大股指均出现超过1%的涨幅。 上证指数...
千亿市值巨头,涨停 马年首个交易日,A股主要指数早盘集体上涨。截至午盘,沪指涨1.17%,深证成指涨1.82%,创业板指...
匈牙利外长:决定向谁买能源是主... 据央视新闻报道,欧盟成员国外长2月23日未能就第20轮对俄罗斯制裁方案达成一致。匈牙利外交与对外经济...
IC外汇平台:日本经济长期疲软... 根据国际清算银行数据,今年1月日元实际有效汇率指数降至67.73,创1973年以来最低。该指数反映日...