原因
在说明什么是热更新之前,我们先说说为什么要使用热更新。
在我们打包之后,甚至使用了webpack-dev-server, 我们会发现,假如,我们在页面中填写了一些数据,然后我们修改了页面的样式,会发现,页面会刷新,填写的数据也会丢失。
所以,热更新的作用就是
- 保留在完全重新加载页面时丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 调整样式更加快速 - 几乎相当于在浏览器调试器中更改样式
我们可以看下例子:
结构
{"name": "webpack-demo","version": "1.0.0","description": "描述","private": true,"scripts": {"start": "webpack-dev-server"},"author": "chenYongRen","license": "ISC","devDependencies": {"autoprefixer": "^10.4.7","clean-webpack-plugin": "^4.0.0","css-loader": "^6.7.1","express": "^4.18.1","file-loader": "^6.2.0","html-webpack-plugin": "^5.5.0","node-sass": "^7.0.1","postcss-loader": "^7.0.0","sass-loader": "^13.0.0","style-loader": "^3.3.1","url-loader": "^4.1.1","webpack": "^5.73.0","webpack-cli": "^4.9.2","webpack-dev-middleware": "^5.3.3","webpack-dev-server": "^4.9.3"},"dependencies": {},"browserslist": ["> 1%","last 2 versions"]}
const path = require('path')const HtmlWebpackPlugin = require('html-webpack-plugin')const { CleanWebpackPlugin } = require("clean-webpack-plugin");module.exports = {mode: 'development',devtool: 'eval',entry: {main: './src/index.js'},devServer: {// 服务器启动在哪个文件夹下static: {directory: path.join(__dirname, 'dist'),},// 自动打开浏览器open: true,// 默认端口号port: 8080},module: {rules:[{test: /\.(jpg|png|gif)$/,use:{loader: 'url-loader',options: {name:'[name]_[hash].[ext]',outputPath: 'images/',limit: 2048 // 2kb}}},{test: /\.scss$/,use:['style-loader',{loader:'css-loader',options: {importLoaders: 2}},'sass-loader','postcss-loader']},{test: /\.css$/,use:['style-loader','css-loader','postcss-loader']}]},plugins:[new HtmlWebpackPlugin({template: 'src/index.html'}), new CleanWebpackPlugin()],output: {// 我所有打包生成的文件之间的引用,前面都加根路径// 其实一般不加也可以publicPath:'/',filename: '[name].js',path: path.resolve(__dirname, 'dist')}}
import './style.css'var btn = document.createElement('button');btn.innerHTML = '新增';document.body.appendChild(btn);btn.onclick = function(){var div = document.createElement('div');div.innerHTML = 'item'document.body.appendChild(div)}
div:nth-of-type(odd){background: yellow;}
效果
如果现在把style.css的背景改成blue,他会重新刷新。
💡但是webpack5不存在这个为题,就算不加任何配置,也能实现热更替的功能。 所以下面方法是针对webpack5以下的版本
在 webpack5以下的版本如果遇到页面的样式修改导致整个页面刷新的问题
就要设置热更新的配置,以及插件
hot: true,
hotOnly: true
并且在webpack5中
hotOnly已经废除。
直接使用hot:’only’
然后重启命令就可以生效了
const path = require('path')const HtmlWebpackPlugin = require('html-webpack-plugin')const { CleanWebpackPlugin } = require("clean-webpack-plugin");// 引入webpack来使用热更新const webpack = require('webpack');module.exports = {mode: 'development',devtool: 'eval',entry: {main: './src/index.js'},devServer: {// 服务器启动在哪个文件夹下static: {directory: path.join(__dirname, 'dist'),},// 自动打开浏览器open: true,// 默认端口号port: 8080,// 让webpack devServer开启 hot Module Replacement模式hot: true,// 即便hmr的功能没有生效,也不让浏览器自动重新刷新hotOnly: true},module: {rules:[{test: /\.(jpg|png|gif)$/,use:{loader: 'url-loader',options: {name:'[name]_[hash].[ext]',outputPath: 'images/',limit: 2048 // 2kb}}},{test: /\.scss$/,use:['style-loader',{loader:'css-loader',options: {importLoaders: 2}},'sass-loader','postcss-loader']},{test: /\.css$/,use:['style-loader','css-loader','postcss-loader']}]},plugins:[new HtmlWebpackPlugin({template: 'src/index.html'}), new CleanWebpackPlugin(),// 热更新插件new webpack.HotModuleReplacementPlugin()],output: {// 我所有打包生成的文件之间的引用,前面都加根路径// 其实一般不加也可以publicPath:'/',filename: '[name].js',path: path.resolve(__dirname, 'dist'),}}
但要说明一点,就是这里的是针对css样式修改,不会改js渲染出来的内容。
接下来我们再看看下面的例子
我们先把热更新关掉
const path = require('path')const HtmlWebpackPlugin = require('html-webpack-plugin')const { CleanWebpackPlugin } = require("clean-webpack-plugin");// 引入webpack来使用热更新const webpack = require('webpack');module.exports = {mode: 'development',devtool: 'eval',entry: {main: './src/index.js'},devServer: {// 服务器启动在哪个文件夹下static: {directory: path.join(__dirname, 'dist'),},// 自动打开浏览器open: true,// 默认端口号port: 8080,// 让webpack devServer开启 hot Module Replacement模式// hot: 'only'},module: {rules:[{test: /\.(jpg|png|gif)$/,use:{loader: 'url-loader',options: {name:'[name]_[hash].[ext]',outputPath: 'images/',limit: 2048 // 2kb}}},{test: /\.scss$/,use:['style-loader',{loader:'css-loader',options: {importLoaders: 2}},'sass-loader','postcss-loader']},{test: /\.css$/,use:['style-loader','css-loader','postcss-loader']}]},plugins:[new HtmlWebpackPlugin({template: 'src/index.html'}), new CleanWebpackPlugin(),// 热更新插件// new webpack.HotModuleReplacementPlugin()],output: {// 我所有打包生成的文件之间的引用,前面都加根路径// 其实一般不加也可以publicPath:'/',filename: '[name].js',path: path.resolve(__dirname, 'dist'),}}
function counter(){var div = document.createElement('div')div.setAttribute('id','counter')div.innerHTML = 1;div.onclick = function(){div.innerHTML = parseInt(div.innerHTML, 10) + 1}document.body.appendChild(div)}export default counter;
function number(){var div = document.createElement('div')div.setAttribute('id','number')div.innerHTML = 1000;document.body.appendChild(div)}export default number;
// import './style.css'// var btn = document.createElement('button');// btn.innerHTML = '新增';// document.body.appendChild(btn);// btn.onclick = function(){// var div = document.createElement('div');// div.innerHTML = 'item'// document.body.appendChild(div)// }import counter from './counter'import number from './number'counter();number();
点击上面的数字会递增,但是如果这是我们修改number.js的数字
会直接清掉上面的数据
然后我们试试把热更新启动
const path = require('path')const HtmlWebpackPlugin = require('html-webpack-plugin')const { CleanWebpackPlugin } = require("clean-webpack-plugin");// 引入webpack来使用热更新const webpack = require('webpack');module.exports = {mode: 'development',devtool: 'eval',entry: {main: './src/index.js'},devServer: {// 服务器启动在哪个文件夹下static: {directory: path.join(__dirname, 'dist'),},// 自动打开浏览器open: true,// 默认端口号port: 8080,// 让webpack devServer开启 hot Module Replacement模式hot: 'only'},module: {rules:[{test: /\.(jpg|png|gif)$/,use:{loader: 'url-loader',options: {name:'[name]_[hash].[ext]',outputPath: 'images/',limit: 2048 // 2kb}}},{test: /\.scss$/,use:['style-loader',{loader:'css-loader',options: {importLoaders: 2}},'sass-loader','postcss-loader']},{test: /\.css$/,use:['style-loader','css-loader','postcss-loader']}]},plugins:[new HtmlWebpackPlugin({template: 'src/index.html'}), new CleanWebpackPlugin(),// 热更新插件new webpack.HotModuleReplacementPlugin()],output: {// 我所有打包生成的文件之间的引用,前面都加根路径// 其实一般不加也可以publicPath:'/',filename: '[name].js',path: path.resolve(__dirname, 'dist'),}}
我们可以发现,他虽然没有刷新,但是它也没有改变
如何解决这个问题。
这就需要module.hot来完成。
// import './style.css'
// var btn = document.createElement('button');
// btn.innerHTML = '新增';
// document.body.appendChild(btn);
// btn.onclick = function(){
// var div = document.createElement('div');
// div.innerHTML = 'item'
// document.body.appendChild(div)
// }
import counter from './counter'
import number from './number'
counter();
number();
// 如果启动了热模块
if(module.hot){
// 如果number这个文件发生变化,就会执行后面的函数
module.hot.accept('./number', ()=>{
number();
})
}


当然这个写法只是给我们了解module.hot.accept的作用。
这样就没问题了
// import './style.css'
// var btn = document.createElement('button');
// btn.innerHTML = '新增';
// document.body.appendChild(btn);
// btn.onclick = function(){
// var div = document.createElement('div');
// div.innerHTML = 'item'
// document.body.appendChild(div)
// }
import counter from './counter'
import number from './number'
counter();
number();
// 如果启动了热模块
if(module.hot){
// 如果number这个文件发生变化,就会执行后面的函数
module.hot.accept('./number', ()=>{
document.body.removeChild(document.getElementById('number'))
number();
})
}
而且功能也实现了
我们这里的时候其实就可以发现,style样式修改我们只需要配置一下热模块就可以了,
但是为什么修改js文件的时候还要使用module.hot.accept这样的代码呢。
原因很简单:
因为module.hot.accept这样的代码,在css-loader里面它已经帮你编写好了,其实就是css-loader底层帮我们实现了这样的代码。
类似vue也是。
但是如果我们的项目之中,遇到比较偏的文件类型,比如数据文件,他们的loader里面并没有这样的内置效果时,就需要我们这样写。
