Appearance
本地开发服务器和HMR
本地开发服务器
开发的时候一般都会启动服务器进行效果预览
- 通过
build
进行打包部署到服务器,进行预览 - 使用
live server
插件启动本地服务,进行预览
上面两种方式都不是很方便,影响开发效率,希望当文件发生变化时,可以自动的完成 编译 和 展示
实现自动编译,webpack 提供了几种方式:
- webpack watch
- webpack-dev-server
- webpack-dev-middleware
webpack watch
在 watch 模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译,不需要手动去运行 npm run build
指令
配置方式有两种:
- 在配置文件中配置
js
module.exports = {
//...
watch: true,
}
- 在命令行中添加
--watch
参数
json
npx webpack --watch
可以在 package.json
中配置 scripts
json
"scripts": {
//...
"watch": "webpack --watch"
},
webpack-dev-server
watch
可以监听到文件的变化,但是事实上它本身是没有自动刷新浏览器的功能的
安装
bash
npm i webpack-dev-server -D
添加一个新的scripts
脚本,在webpack5 中直接使用 webpack serve
即可
json
"scripts": {
"serve": "webpack serve --open"
},
可以在 config
文件中对 devServer
进行配置
js
// webpack.config.js
module.exports = {
//...
devServer: {
compress: true,
port: 8010,
open: true,
hot: true // 开启热更新
}
}
webpack-dev-server
在编译之后不会写入到任何输出文件。而是将 bundle
文件保留在内存中
事实上webpack-dev-server
使用了一个库叫memfs(memory-fs
webpack
自己写的)
devServer常见的配置:
属性 | 作用 |
---|---|
compress | 为服务启动 gzip 压缩 |
devMiddleware | webpack-dev-middleware 的一些配置 |
host | 主机地址 |
hot | 开启模块热更新 |
open | 自动打开默认浏览器 |
port | 端口号 |
proxy | 代理 |
server | 设置服务器['http' | 'https' | 'spdy'](默认为http) |
webpack-dev-middleware
webpack-dev-middleware
是一个封装器(wrapper),它可以把 webpack
处理过的文件发送到一个 server
webpack-dev-server
在内部使用了它,然而它也可以作为一个单独的 package
来使用,以便根据需求进行更多自定义设置
安装(这里使用的 express,也可以使用其他的)
bash
npm i -D express webpack-dev-middleware
使用
js
// ./src/server.js
const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')
const app = express()
const config = require('../webpack.config.js')
const compiler = webpack(config)
// 使用 webpack-dev-middleware 中间件
app.use(webpackDevMiddleware(compiler, {}))
app.listen(8010, () => {
console.log('server is running on http://localhost:8010')
})
bash
node server.js
HMR 热更新
HMR 全称 Hot Module Replacement
,可以翻译为「模块热更新」
它能够在保持页面状态不变的情况下动态替换、删除、添加代码模块,提供超级丝滑顺畅的 Web 页面开发体验。
使用
只需要简单的配置,就能启用 HMR
功能
- 设置
devServer.hot
为true
js
// webpack.config.js
module.exports = {
//...
devServer: {
hot: true // 开启热更新
}
}
打开了会发现,修改了某一处的代码,依然会刷新整个页面
还需要使用module.hot.accept
指定哪些模块发生更新时,进行HMR
js
// ./src/index.js
let render = () => {
let title = require('./title.js');
document.getElementById('root').innerText = title;
}
render();
if (module.hot) {
//当title.js模块发生修改的时候,执行render方法这个回调函数
module.hot.accept(["./title.js"], render);
}
js
// ./src/title.js
module.exports = 'title'
但是在实际开发中,不可能老是去配置module.hot.accept
,效率也不高
在我们常用的 Vue、React框架中都有开箱即用的方案
框架中的HMR
- vue开发中,我们使用vue-loader,此loader支持vue组件的HMR
- react开发中,有React Hot Loader,实时调整react组件(目前改用 react-refresh)
vue 中的配置
bash
npm install vue-loader vue-template-compiler -D
js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
//...
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin()
]
}
react 中的配置
bash
npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh babel-loader @babel/preset-env @babel/preset-react
js
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
module.exports = {
//...
module: {
rules: [
{
test: /\.js|jsx$/,
use: 'babel-loader',
exclude: /node_modules/,
presets: [
['@babel/preset-env'],
['@babel/preset-react']
],
plugins: [
['react-refresh/babel']
]
}
]
},
plugins: [
new ReactRefreshWebpackPlugin()
]
}
实现原理
Webpack HMR
特性的执行过程并不复杂,核心:
- 使用
webpack-dev-server
(后面简称 WDS)托管静态资源,同时以Runtime
方式注入一段处理HMR
逻辑的客户端代码; - 浏览器加载页面后,与
WDS
建立WebSocket
连接; Webpack
监听到文件变化后,增量构建发生变更的模块,并通过WebSocket
发送hash
事件;- 浏览器接收到
hash
事件后,请求manifest
资源文件,确认增量变更范围; - 浏览器加载发生变更的增量模块;
Webpack
运行时触发变更模块的module.hot.accept
回调,执行代码变更逻辑;- 完成。
原理图: