webpack-dev-server和HMR原理

webpack-dev-server

webpack-dev-server只用于开发,不能在生产环境中使用。
webpack-dev-server其实是给你提供了一个简单的web服务器,使得打包后的文件能够在该服务器上运行,并且实现了实时更新的功能。
配置dev-server之后,我们改动css、js文件之后保存,webpack-dev-server会自动帮我们打包文件到内存,并且自动帮我们刷新页面,改变页面视图。
我们可以看到浏览器左上角的图标自动发生变化,表明页面进行了刷新。
在这里插入图片描述
当页面的内容较多时,刷新页面变得很浪费时间,或者当我们打开一个弹窗时,我们希望改变弹窗上的某一个视觉,在弹窗不关闭的情况下,能够更新页面部分视图,这就需要配置热更新(HMR)。
webpack.config.js 配置:
在这里插入图片描述
上面两项配置了热更新,重新打包,重新npm start,再改变css样式,保存,发现图标并没有转动,说明页面没有刷新,但是页面上的样式变了,这就是热更新,只替换发生变化的模块。

模块热更新的原理(HMR)

HMR帮助我们做了什么?
使用webpack-dev-server之前,当我们修改文件之后,我们需要手动重新打包,然后刷新浏览器才能看到我们改动的内容,而且还需要恢复之前页面的状态(比如重新打开弹窗)。webpack-dev-server配置了HMR之后,这些HMR都替我们做了。
接下来,我们来讨论一下HMR的工作原理:
在这里插入图片描述
上图是webpack和webpack-dev-server之间配合进行模块热更新的原理图。
上图底部红色框内是服务端,而上面的橙色框是浏览器端。
绿色的方框是 webpack 代码控制的区域。蓝色方框是 webpack-dev-server 代码控制的区域,洋红色的方框是文件系统,文件修改后的变化就发生在这,而青色的方框是应用本身。
上图表示了从代码修改到模块热更新的整个过程,图中的数字已经表示了整个过程。
第一、二步:
是weback和webpack-dev-server之间的交互,是webpack-dev-server的中间件webpack-dev-middleware通过webpack暴露的api来watch监听文件的变化,当文件发生变化时,webpack重新打包,并告知webpack将打包后的文件存放在内存中。
问题1:为什么要将打包后的文件存放在内存中,而不是文件目录下?
webpack-dev-middleware将原本webpack的outputFileSystem 替换成了MemoryFileSystem 实例,这样打包后的文件就存放在内存中了,访问内存中的文件比访问文件目录下的文件更快,而且减少了代码写入文件的开销,便于开发。
第三步:
webpack-dev-server监听文件变化时,只会监听contentBase下的js,css文件等,不会监听html等静态文件的变化,当设置devserver.watchContentBase为true时,webpack-dev-server也会监听html等静态文件的变化,当发生变化时,会通知浏览器进行
刷新
(并不是HMR)。
第四步:
是webpack-dev-server内部的webpack-dev-middleware和client之间通过sockjs在浏览器和服务器之间建立websocket链接,webpack-dev-server会监听webpack的compile的done事件,当编辑完成后,把html文件是否发生变化、最新打包的hash值传递给浏览器端,浏览器根据这些来作出相应的操作。
第五步:
client负责接收服务端发送过来的信息,包括hash值和ok消息,先将hash值存放起来,当接收到ok值之后,根据hot的配置,来决定是否进行下一步热更新。如果有hot的配置,就调用webpack/hot/emitter将最新的hash值传递给webpack,将热更新的控制权交给webpack,如果没有配置热更新,就调用location.reload方法直接刷新页面。

// webpack-dev-server/client/index.js
hash: function msgHash(hash) {
    currentHash = hash;
},
ok: function msgOk() {
    // ...
    reloadApp();
},
// ...
function reloadApp() {
  // ...
  if (hot) {
    log.info('[WDS] App hot update...');
    const hotEmitter = require('webpack/hot/emitter');
    hotEmitter.emit('webpackHotUpdate', currentHash);
    // ...
  } else {
    log.info('[WDS] App updated. Reloading...');
    self.location.reload();
  }
}

在这里插入图片描述

第六步:
HotModuleReplacement.runtime是HMR的中枢,接受到上一步传递给他的hash值,它通过JsonpMainTemplate.runtime向服务器发送ajax、jsonp请求,分别请求更新的文件列表、最新的代码模块,返回给HMR runtime,进行模块热更新。
在这里插入图片描述
上图中,蓝色部分是我改动某个js文件之后保存之后,发送到浏览器的websocket信息,client接受到ok的信息之后,根据hot设置将hash值发送到webpack。webpack的三个模块通过ajax和jsonp请求,请求了改动的文件列表和改动的模块代码(图中绿色的部分),HMR runtime,依此进行模块热更新。

问题2:
为什么更新模块的代码不通过websocket直接发送到浏览器,而是要通过webpack来获取数据?
功能解耦,各司其职,client值负责消息的传递,而不负责新模块的获取,这些工作是由HMR runtime来做的,这样做的话,我们就算不用webpack-dev-server,也可以用webpack-hot-middleware和webpack配合来实现热更新。

第七步:
最后一步就是HMR runtime进行热更新。热更新主要分为三步:找到变化的模块和依赖,删除过期的模块和依赖,将新的模块添加到modules中。
问题3:
当模块的热替换过程中,如果替换模块失败,有什么回退机制吗?
当替换失败时,直接刷新页面。

第八步:
实现了热更新之后,其实我们的业务代码并不知道打包代码发生了变化,我们需要重新添加模块更新后的处理函数,将代码的变化更新到页面上。

// index.js
if(module.hot) {
    module.hot.accept('./hello.js', function() {
        div.innerHTML = hello()
    })
}

参考文章:https://zhuanlan.zhihu.com/p/30669007

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值