HMR
解释
HMR全称 Hot Module Replacement
模块热替换指应用程序运行过程中,替换、添加、删除模块,而无需刷新整个页面
HMR作用
不重新加载整个页面,可以保留应用程序一些状态不丢失
只更新需要变化的内容,效率高
修改了css、js源代码,会立即在浏览器更新,相当于在浏览器的 devtool 直接修改
使用
保证当前运行的是 webpack-dev-server,而不是webpack的watch,也不是 webpack-dev-middleware
在webpack.config.js中增加devServer选项配置 module.exports = { // 专门为 webpack-dev-server 配置的 devServer: { hot: true } }
此时启动服务,浏览器会提示:
[webpack-dev-server] Hot Module Replacement enabled.此时,在入口文件,还需要指定哪些模块需要使用 HMR:
import './math.js'
console.log('Hello WebPack')
// 如果希望 math.js 启用热更新:
if(module.hot){
module.hot.accept('./math.js', () => { // 参数1 可以是数组,也可以写多个 module.hot.accept,参数2是回调函数,当模块热更新完毕执行
console.log('模块更新完毕')
})
}问题来了:如果每次每个需要热更新的模块,都写一遍,非常麻烦,react-refresh、vue-loader 都支持 HMR
React 的 HMR
在 src 新建 App.jsx
import React, {Component} from 'react'
class App extends Component {
constructor(props) {
super(props)
this.state = {
message: 'hello react'
}
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
</div>
)
}
}
export default App;安装react
npm install react react-dom在入口文件中引入刚写的模块,并渲染
import './math.js'
import React from 'react'
import ReactApp from './App.jsx'
React.render(<ReactApp/>, document.getElementById('app'))安装 webpack相关的 react 解析器等
npm install @babel/core babel-loader @babel/preset-env @babel/preset-react -D配置 webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './build')
},
module: {
rules: [
{
test: /\.jsx?$/i,
use: 'babel-loader'
}
],
},
plugins: [
new HtmlWebpackPlugin({
title: '页面标题',
template: './public/index.html'
})
]
}配置 babel.config.js
module.exports = {
presets: [
['@babel/preset-env'],
['@babel/preset-react']
]
}package.json 配置
"scripts": {
"build": "webpack",
"dev": "webpack serve"
},执行 npm run dev
注意:此时,react并不能实现热更新,还是刷新整个页面。
安装 react-refresh-webpack-plugin
npm install @pmmmwh/react-refresh-webpack-plugin react-refresh -D在 webpack.config.js 中使用 react-refresh-webpack-plugin 插件
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './build')
},
module: {
rules: [
{
test: /\.jsx?$/i,
use: 'babel-loader'
}
],
},
plugins: [
new HtmlWebpackPlugin({
title: '页面标题',
template: './public/index.html'
}),
new ReactRefreshWebpackPlugin()
]
}配置 babel.config.js
module.exports = {
presets: [
['@babel/preset-env'],
['@babel/preset-react']
],
plugins: [
['react-refresh/babel']
]
}此时,再次运行就可以实现react组件的 HMR
Vue 的 HMR
安装 Vue
npm install vue在根目录新建 App.vue
<template>
<div id="app">
<h2 class="title">{{message}}</h2>
</div>
</template>
<script>
export default {
data () {
return {
message: '111'
}
}
}
</script>
<style scoped>
.title{
color: red;
}
</style>在入口文件导入 App.vue 组件 import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')
安装 vue-loader 处理 vue文件,安装 vue-template-compiler 处理template模板
npm install vue-loader vue-template-compiler -D因为刚才的模块里有css,所以要安装 css-loader、style-loader
npm install css-loader style-loader -Dwebpack.config.js 配置
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './build')
},
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader'
],
},
{
test: /\.js$/i,
use: 'babel-loader'
},
{
test: /\.vue$/i,
use: 'vue-loader'
}
],
},
plugins: [
new HtmlWebpackPlugin({
title: '页面标题',
template: './public/index.html'
}),
new VueLoaderPlugin()
]
}执行 npm run dev
此时,就实现了vue 的 HMR,因为 vue-loader 内部实现了 vue 的 HMR,不需要额外配置,也不需要额外安装插件。
HMR 实现的原理
webpack-dev-server 会创建两个服务:提供静态资源的服务(express)和Socket服务(net.Socket)
express server负责直接提供静态资源服务(打包后的资源直接被浏览器请求和解析)
HMR Socket Server 是一个socket长连接: 长连接的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端) 当服务器监听到对应的模块发生变化时,会生成.json(manifest文件)和.js(update chunk) 通过长连接,可以直接将这两个文件主动发送给客户端(浏览器) 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新
