Skip to content

本地开发服务器和HMR

本地开发服务器

开发的时候一般都会启动服务器进行效果预览

  • 通过 build 进行打包部署到服务器,进行预览
  • 使用 live server 插件启动本地服务,进行预览

上面两种方式都不是很方便,影响开发效率,希望当文件发生变化时,可以自动的完成 编译 和 展示

实现自动编译,webpack 提供了几种方式:

  • webpack watch
  • webpack-dev-server
  • webpack-dev-middleware

webpack watch

在 watch 模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译,不需要手动去运行 npm run build 指令

配置方式有两种:

  1. 在配置文件中配置
js
module.exports = {
  //...
  watch: true,
}
  1. 在命令行中添加 --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使用了一个库叫memfsmemory-fs webpack自己写的)

devServer常见的配置:

属性作用
compress为服务启动 gzip 压缩
devMiddlewarewebpack-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 功能

  1. 设置 devServer.hottrue
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 特性的执行过程并不复杂,核心:

  1. 使用 webpack-dev-server (后面简称 WDS)托管静态资源,同时以 Runtime 方式注入一段处理 HMR 逻辑的客户端代码;
  2. 浏览器加载页面后,与 WDS 建立 WebSocket 连接;
  3. Webpack 监听到文件变化后,增量构建发生变更的模块,并通过 WebSocket 发送 hash 事件;
  4. 浏览器接收到 hash 事件后,请求 manifest 资源文件,确认增量变更范围;
  5. 浏览器加载发生变更的增量模块;
  6. Webpack 运行时触发变更模块的 module.hot.accept 回调,执行代码变更逻辑;
  7. 完成。

原理图:

image-20240828114520228

Released under the MIT License.