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 -D
webpack.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机制,加载这两个文件,并且针对修改的模块进行更新