HotModuleReplacement(热模替换)

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

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

什么是HotModuleReplacement(热模替换)

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

使用热模替换前:(开发服务器 => hot:false)

image

  • 关闭热模替换后,每一次改动样式代码,页面重新加载

使用热模替换前:(开发服务器 => hot:true)

修改样式过后页面不再刷新就能实现变换

  • 开启热模替换后,每一次改动样式代码,页面不会重新加载,只会重新加载相对应的修改过后的模块并在控制台输出

热模替换的使用(hot配置)

样式的基本配置

// 配置开发服务器: 不会输出任何资源, 在内存中的编译打包的*******************************
devServer: {
host: "localhost", // 启动服务器域名
port: "5000", // 启动服务器端口号
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");//开启对应路径中js的热模替换
module.hot.accept("./js/sum.js");//开启对应路径中js的热模替换
}
  • 此时我们改动对应的js代码,浏览器同样不会重新加载,只会输出更改过后的模块

    • count.js

image

  • sum.js
    image

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

  • 比如:vue-loader , react-hot-loader, 这样在我们的实际开发中,就不需要再main.js中一点一点的写, 方便我们项目的开发

OneOf

为什么需要OneOf

  • 打包时每个文件都会经过所有 loader 处理,虽然因为 test 正则原因实际没有处理上,但是都要过一遍。比较慢。
  • 也就是说,当一个css文件进来的时候, 这个文件会经过所有的loader,尽管你第一个loader就是处理css样式的,但是它还会继续的向下进行判断,这样就会导致打包时间被延长了,因为它做了很多无用功
  • 我们希望打包的时候,在文件经过其第一个处理该文件类型的loader时,进行打包,不需要再向下过其他的loader,这时就需要OneOf了。

什么是OneOf

  • 顾名思义就是只能匹配上一个 loader, 剩下的就不匹配了。

使用方法:(开发模式和生产模式均可使用)

// 使用的是commonjs 的语法格式[node.js]
const path = require("path");//node.js中的核心模块,专门用于处理路径问题
// 引入eslint
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
// 引入HtmlWebpackPlugin插件
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
// 入口
// 相对路径和绝对路径都行
entry:"./src/main.js",//相对路径
// 输出
output:{
// 所有文件的输出目录,必须是绝对路径
path: undefined, // 开发模式没有输出(因为存在开发服务器,不存在输出),不需要指定输出目录
// js文件(入口文件)输出的文件名(打包后在输出路径当中生成的文件夹名)
filename:"static/js/main.js",//所以我们这里改成js资源就输出到一个js文件夹当中
// clean: true, // 开发模式没有输出,不需要清空输出结果(开发服务器)
},
// 加载器
module:{
rules:[
// loder的配置
{
// oneOf配置, 每个文件只能被其中一个loader处理(第一个遇到的)***********************************
oneOf: [
// 1.处理css资源
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: [
"style-loader", //将js中css通过创建style标签添加html文件当中生效
"css-loader"//将css资源编译成common.js的模块到js当中
],
},
// 2.处理less资源
{
test: /\.less$/,
// loader:xxx => 自能使用一个loader
use: [
"style-loader",
"css-loader",
"less-loader"//将less编译成css文件
], // use能够使用多个loader
},
// 3.处理图片资源
{
test: /\.(png|jpe?g|gif|webp)$/,//正则判断图片后缀
type: "asset",
parser:{
dataUrlCondition:{
// 将小于10kb的图片转化为base64
// 优点:减小请求数量 缺点:原图片的体积会变大(故大体积突变不会使用这种方法)
maxSize: 10*1024//10kb(大体积图片不会使用这种方法)
}
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: 'static/imgs/[hash:8][ext][query]'
}
},
// 4.处理字体资源
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",//这里的是改成"asset/resource"
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
// 配置babel**************************************************
{
test: /\.js$/,//检查匹配以js结尾的文件
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
// **********************************************************
]
// ***********************************************************************
}
],
},
// 插件
plugins:[
// plugin的配置

// eslint配置
new ESLintWebpackPlugin({
// 指定检查文件的根目录(src目录下的所有文件的语法)
context: path.resolve(__dirname, "../src"),
}),

// HtmlWebpackPlugin配置*******************************************************
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "../public/index.html"),
}),
// **************************************************************************
],

// 配置开发服务器: 不会输出任何资源, 在内存中的编译打包的*******************************
devServer: {
host: "localhost", // 启动服务器域名
port: "5000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
hot: true, // 关闭HMR功能(默认开启,只能用于开发环境,生产环境不需要了)
},
// *****************************************************************************
// 模式
mode:"development",//开发模式
devtool: "cheap-module-source-map",//sourceMap开发模式
};

Include&Exclude

为什么需要Include/Exclude

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

什么是Include&Exclude

  • include

    • 包含,只处理 xxx 文件
  • exclude

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

基本使用

// 使用的是commonjs 的语法格式[node.js]
const path = require("path");//node.js中的核心模块,专门用于处理路径问题
// 引入eslint
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
// 引入HtmlWebpackPlugin插件
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
// 入口
// 相对路径和绝对路径都行
entry:"./src/main.js",//相对路径
// 输出
output:{
// 所有文件的输出目录,必须是绝对路径
path: undefined, // 开发模式没有输出(因为存在开发服务器,不存在输出),不需要指定输出目录
// js文件(入口文件)输出的文件名(打包后在输出路径当中生成的文件夹名)
filename:"static/js/main.js",//所以我们这里改成js资源就输出到一个js文件夹当中
// clean: true, // 开发模式没有输出,不需要清空输出结果(开发服务器)
},
// 加载器
module:{
rules:[
// loder的配置
{
// oneOf配置, 每个文件只能被其中一个loader处理(第一个遇到的)***********************************
oneOf: [
// 1.处理css资源
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: [
"style-loader", //将js中css通过创建style标签添加html文件当中生效
"css-loader"//将css资源编译成common.js的模块到js当中
],
},
// 2.处理less资源
{
test: /\.less$/,
// loader:xxx => 自能使用一个loader
use: [
"style-loader",
"css-loader",
"less-loader"//将less编译成css文件
], // use能够使用多个loader
},
// 3.处理图片资源
{
test: /\.(png|jpe?g|gif|webp)$/,//正则判断图片后缀
type: "asset",
parser:{
dataUrlCondition:{
// 将小于10kb的图片转化为base64
// 优点:减小请求数量 缺点:原图片的体积会变大(故大体积突变不会使用这种方法)
maxSize: 10*1024//10kb(大体积图片不会使用这种方法)
}
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: 'static/imgs/[hash:8][ext][query]'
}
},
// 4.处理字体资源
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",//这里的是改成"asset/resource"
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
// 配置babel**********************************************************
{
test: /\.js$/,//检查匹配以js结尾的文件

// Include/Exclude*******************************************
// exclude: /node_modules/, // 排除node_modules代码不编译
include: path.resolve(__dirname, "../src"), // 也可以用包含(两者只能用其一)
// **********************************************************

loader: "babel-loader",
},
// *****************************************************************
]
// ***************************************************************************
}
],
},
// 插件
plugins:[
// plugin的配置

// eslint配置
new ESLintWebpackPlugin({
// 指定检查文件的根目录(src目录下的所有文件的语法)
context: path.resolve(__dirname, "../src"),

// Include/Exclude******************
exclude: "node_modules", // 默认值
// ********************************
}),

// HtmlWebpackPlugin配置*******************************************************
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "../public/index.html"),
}),
// ****************************************************************************
],

// 配置开发服务器: 不会输出任何资源, 在内存中的编译打包的*******************************
devServer: {
host: "localhost", // 启动服务器域名
port: "5000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
hot: true, // 关闭HMR功能(默认开启,只能用于开发环境,生产环境不需要了)
},
// **********************************************************************************
// 模式
mode:"development",//开发模式
devtool: "cheap-module-source-map",//sourceMap开发模式
};
  • 生产模式也是如此

总结:

  • 一般来讲,在项目当中我么们只需要针对js文件做处理,也就是BabelESLint, 样式是不需要做Include&Exclude处理的,为什么呢? 因为在开发的时候我们很少去引入第三方的样式, 一般都是我们自己写样式, 因此我们就自然就不需要去排除它了, 其次,尽管我们引入了如Bootstrap这样的第三方样式,但是我们最终是希望它和我们最终的样式打包到一起的, 因此我们仍然是要处理的, 所以就不存在什么包含和非包含了

Cache缓存

为什么需要Cache缓存?

  • 每次打包时 js 文件都要经过 Eslint 检查 和 Babel 编译,要知道, 打包其实所用的事件大部分是用在js的编译上面, 诸如css等这些样式上的打包速度是很快的, 因此我们对打包速度进行一些优化多数是在js的打包中。
  • 我们可以缓存之前的 Eslint 检查 和 Babel 编译结果,这样第二次打包时速度就会更快了。

什么是Cache

  • Eslint 检查 和 Babel 编译结果进行缓存。
  • 作用就是在我们第二次打包项目的时候不需要再对整个项目的js问价你重新打包, 只需要打包更改过后的即可

基本使用:

// 使用的是commonjs 的语法格式[node.js]
const path = require("path");//node.js中的核心模块,专门用于处理路径问题
// 引入eslint
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
// 引入HtmlWebpackPlugin插件
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
// 入口
// 相对路径和绝对路径都行
entry:"./src/main.js",//相对路径
// 输出
output:{
// 所有文件的输出目录,必须是绝对路径
path: undefined, // 开发模式没有输出(因为存在开发服务器,不存在输出),不需要指定输出目录
// js文件(入口文件)输出的文件名(打包后在输出路径当中生成的文件夹名)
filename:"static/js/main.js",//所以我们这里改成js资源就输出到一个js文件夹当中
// clean: true, // 开发模式没有输出,不需要清空输出结果(开发服务器)
},
// 加载器
module:{
rules:[
// loder的配置
{
// oneOf配置, 每个文件只能被其中一个loader处理(第一个遇到的)********************
oneOf: [
// 1.处理css资源
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: [
"style-loader", //将js中css通过创建style标签添加html文件当中生效
"css-loader"//将css资源编译成common.js的模块到js当中
],
},
// 2.处理less资源
{
test: /\.less$/,
// loader:xxx => 自能使用一个loader
use: [
"style-loader",
"css-loader",
"less-loader"//将less编译成css文件
], // use能够使用多个loader
},
// 3.处理图片资源
{
test: /\.(png|jpe?g|gif|webp)$/,//正则判断图片后缀
type: "asset",
parser:{
dataUrlCondition:{
// 将小于10kb的图片转化为base64
// 优点:减小请求数量 缺点:原图片的体积会变大(故大体积突变不会使用这种方法)
maxSize: 10*1024//10kb(大体积图片不会使用这种方法)
}
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: 'static/imgs/[hash:8][ext][query]'
}
},
// 4.处理字体资源
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",//这里的是改成"asset/resource"
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
// 配置babel*****************************************
{
test: /\.js$/,//检查匹配以js结尾的文件

// Include/Exclude************************
// exclude: /node_modules/, // 排除node_modules代码不编译
include: path.resolve(__dirname, "../src"), // 也可以用包含(两者只能用其一)
// **************************************

loader: "babel-loader",

//cache缓存********************************
options: {
cacheDirectory: true, // 开启babel编译缓存
cacheCompression: false, // 缓存文件不要压缩
},
// ***************************************
},
// ************************************************
]
// ***************************************************************
}
],
},
// 插件
plugins:[
// plugin的配置

// eslint配置
new ESLintWebpackPlugin({
// 指定检查文件的根目录(src目录下的所有文件的语法)
context: path.resolve(__dirname, "../src"),

// Include/Exclude******************
exclude: "node_modules", // 默认值
// ********************************

// cache缓存设置*******************
cache: true, // 开启缓存
// 缓存目录(eslint缓存存放的路径)
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
// ******************************
}),

// HtmlWebpackPlugin配置*******************************************************
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "../public/index.html"),
}),
// ****************************************************************************
],

// 配置开发服务器: 不会输出任何资源, 在内存中的编译打包的*******************************
devServer: {
host: "localhost", // 启动服务器域名
port: "5000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
hot: true, // 关闭HMR功能(默认开启,只能用于开发环境,生产环境不需要了)
},
// **********************************************************************************
// 模式
mode:"development",//开发模式
devtool: "cheap-module-source-map",//sourceMap开发模式
};

生成的文件.cache

image

Thead

为什么需要Thead

  • 当项目越来越庞大时,打包速度越来越慢,甚至于需要一个下午才能打包出来代码。这个速度是比较慢的。
  • 我们想要继续提升打包速度,其实就是要提升 js 的打包速度,因为其他文件都比较少。
  • 而对 js 文件处理主要就是 eslintbabelTerser(内置的,一般不需要我们去配置) 三个工具,所以我们要提升它们的运行速度。
  • 我们可以开启多进程同时处理 js 文件,这样速度就比之前的单进程打包更快了。

什么是Thrad

  • 多进程打包:开启电脑的多个进程同时干一件事,速度更快。
  • 需要注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为 600ms 左右开销。因此如果项目体积过小的话反而会拖慢打包速度

基本使用:

  • 首先我们要获取我们的cpu数量,我们启动进程的数量就是我们 CPU 的核数。
  1. 如何获取 CPU 的核数,因为每个电脑都不一样。
// nodejs核心模块,直接使用
const os = require("os");
// cpu核数
const threads = os.cpus().length;
  1. 下载包
npm i thread-loader -D
  1. 使用(这里展示生产模式, 开发模式不需要压缩,即不需要配置Terser)
// 使用的是commonjs 的语法格式[node.js]
const path = require("path");//node.js中的核心模块,专门用于处理路径问题
// 获取os内置模块
const os = require("os");
// 引入eslint
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
// 引入HtmlWebpackPlugin插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 引入MiniCssExtractPlugin插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 引入CssMinimizerPlugin插件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// 引入Terser内置插件
const TerserPlugin = require("terser-webpack-plugin");

// 获取cpu核数
const threads = os.cpus().length;

// 设置一个函数用来获取样式处理的loader(提高代码复用率)
function getStyleLoader(pre){//pre为其他的loader,如less-loader
return [
// *************************************************************************
MiniCssExtractPlugin.loader, //将style-loder改成MiniCssExtractPlugin.loader
// ************************************************************************
"css-loader",//将css资源编译成common.js的模块到js当中

// postcss-loader处理css样式的兼容性问题(放在cssloder后面,lessloder前面)*****
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
// ************************************************************************
pre,
].filter(Boolean)//设置一个布尔值的filter来过滤掉undefined(存css样式不需要pre[即其他的loader])
}

module.exports = {
// 入口
// 相对路径和绝对路径都行
entry:"./src/main.js",//相对路径
// 输出
output:{
// 所有文件的输出目录,必须是绝对路径
// path.resolve()方法返回一个绝对路径
// __dirname为node.js中的变量,代表当前文件的文件夹名(就是这个文件夹的webpack_code)
path: path.resolve(__dirname , "../dist"),//相较于开发模式,生产模式需要输出
// js文件(入口文件)输出的文件名(打包后在输出路径当中生成的文件夹名)
filename:"static/js/main.js",//所以我们这里改成js资源就输出到一个js文件夹当中
clean: true,//需要输出就需要clean(自动清空上次打包内容) 原理:在打包前,将path整个目录内容清空,在进行打包
},
// 加载器
module:{
rules:[
// loder的配置
{
// oneOf配置, 每个文件只能被其中一个loader处理(第一个遇到的)************************
oneOf: [
// 1.处理css资源
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: getStyleLoader()
},
// 2.处理less资源
{
test: /\.less$/,
// loader:xxx => 自能使用一个loader
use: getStyleLoader("less-loader")
},
// 3.处理图片资源
{
test: /\.(png|jpe?g|gif|webp)$/,//正则判断图片后缀
type: "asset",
parser:{
dataUrlCondition:{
// 将小于10kb的图片转化为base64
// 优点:减小请求数量 缺点:原图片的体积会变大(故大体积突变不会使用这种方法)
maxSize: 10*1024//10kb(大体积图片不会使用这种方法)
}
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: 'static/imgs/[hash:8][ext][query]'
}
},
// 4.处理字体资源
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",//这里的是改成"asset/resource"
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
// 配置babel***********************************************
{
test: /\.js$/,//检查匹配以js结尾的文件
// Include/Exclude*******************************************
// 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:[
// plugin的配置

// eslint配置
new ESLintWebpackPlugin({
// 指定检查文件的根目录(src目录下的所有文件的语法)
context: path.resolve(__dirname, "../src"),
// Include/Exclude******************
exclude: "node_modules", // 默认值
// ********************************
cache: true, // 开启缓存
// 缓存目录
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
// 开启多线程打包编译***************
threads, // 开启多进程
// *******************************
}),

// HtmlWebpackPlugin配置*******************************************************
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(),
// ******************************************************************************
],

// 开启多线程需要重新手写 Terser 内置模块*********************************************
// webpack5一般指定压缩地方为这里,上面也可以
optimization: {
minimize: true,
minimizer: [
// css压缩也可以写到optimization.minimizer里面,效果一样的
new CssMinimizerPlugin(),//压缩css
// 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了
new TerserPlugin({//内置模块压缩js
parallel: threads // 开启多进程
})
],
},
// **********************************************************************************
/*
生产模式不需要开发服务器(devServer)
*/
// 模式
mode:"production",//生产模式
devtool: "source-map",//sourceMap生产模式
};