参考链接

HMR之前

在 HMR 之前,应用的加载、更新是一种页面级别的原子操作,即使只是单个代码文件发生变更都需要刷新整个页面才能最新代码映射到浏览器上,这会丢失之前在页面执行过的所有交互与状态,例如:

  • 对于复杂表单场景,这意味着你可能需要重新填充非常多字段信息;
  • 弹框消失,你必须重新执行交互动作才会重新弹出。

再小的改动,例如更新字体大小,改变备注信息都会需要整个页面重新加载执行,影响开发体验。引入 HMR 后,虽然无法覆盖所有场景,但大多数小改动都可以实时热更新到页面上,从而确保连续、顺畅的开发调试体验,对开发效率有较大增益效果。

什么是HMR?

  • HMR(Hot Module Replacement),翻译为模块热替换
  • 模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面


HMR通过如下几种方式,来提高开发的速度

  • 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失;
  • 只更新需要变化的内容,节省开发的时间;
  • 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式。

如何使用HMR呢?

  • 默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可;
  • 在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading。

1)开启HMR

  1. module.exports = {
  2. devServer: {
  3. static: './dist',
  4. hot: true
  5. },
  6. }

**注意!!!目前 devServer 和 browerslist 工具有冲突,需要在 webpack.config.js 中加入 target: “web

  1. module.exports = {
  2. target: 'web'
  3. }

然后执行 npm run server就能看到热更新提示。
image.png

但是,你会发现,当我们修改了某一个模块的代码时,依然是刷新整个页面,这是因为我们需要去指定哪些模块发生更新时,进行HMR !!!

  1. if (module.hot) {
  2. module.hot.accept("./math.js", () => {
  3. console.log("math模块发生了更新~");
  4. });
  5. }

2)HMR的原理

Q:那么HMR的原理是什么呢?如何可以做到只更新一个模块中的内容呢?
webpack-dev-server会创建两个服务:提供静态资源的服务express) 和 Socket 服务net.Socket

① express server

负责直接提供静态资源的服务(即打包后的资源直接被浏览器请求和解析)。

② HMR Socket Server

HMR Socket Server是一个socket的长连接。

  • 长连接有一个好处是:建立连接后,双方可以通信(服务器可以直接发送文件到客户端);
  • 服务器监听到对应的模块发生变化时,会生成两个文件:.json文件(是manifest文件)和.js文件update chunk);
  • 通过长连接,可以直接将这两个文件主动发送给客户端(如浏览器);
  • 浏览器拿到这两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新。

图 HMR的原理图
image.png

框架的HMR

Q:在开发其他项目时,我们是否需要经常手动写入module.hot.accept相关的API呢?
比如:开发Vue、React项目,我们修改了组件,希望进行HMR,这个时候应如何去操作呢?事实上,社区已经针对这些有很成熟的解决方案了。

  • 比如Vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验;
  • 比如React开发中,我们有react-hot-loader,用于实时调整react组件(目前React官方已经启用了,改成使用reractrefresh);

接下来,我们分别对Vue、React实现以下HMR功能。

1)Vue中的HMR

Vue的加载我们需要使用vue-loader,而vue-loader加载的组件默认会帮助我们进行HMR的处理。(之前已讲过vue-loader了)

2)React中的HMR(暂略)