【webpack系列】5. 从源码角度分析webpack热更新流程

852 阅读5分钟

之前文章从源码简单详细介绍了webpack整体流程、loader触发时间和过程、模块解析原理和plugin原理核心的知识,这篇文章来说说webpack的热更新相关原理,解开心中的一些疑惑。

1. 什么是热更新

简单来说就是我们在开发阶段,当改动代码时不需要手动编译也不需要刷新浏览器就可以看到效果。再来看下官方定义:简称HMR(Hot Module Replacement),无需完全刷新整个页面就可以更新模块。相信一定很好奇,这是怎么做到的?(先继续往后看)

2. 为什么需要热更新

假设没有热更新,我们想看到改动代码后的效果,每次改动都需要重新打包,随着项目越来越大,编译速度也会比较长,将严重影响开发效率,估计都想要砸电脑了。因此热更新是很有必要的,它的好处就是节省开发时间和提升开发体验。

3. webpack是如何进行热更新的

基于现象来分析会好理解些,先来看下当我们改变业务代码时,肉眼可见都发生了什么?

1)开发工具触发重新编译;

2c670655d1dde131bad90f309baf22be.png

重新编译后增加了两个文件,这两个文件怎么生成的?其实是webpack内置热更新插件注入的,文件名没有配置就使用命名的,如图:

77d923d245e3917fb5f35be93648f623.png

看到这个地方心中瞬间疑惑解决了不少,原来框架工具源码和我们写业务逻辑类似,任何代码都是有依据的。

2)从浏览器网络看到请求了两个文件:第一次请求xxx.hot-update.json文件,得到模块名和最新的hash,然后根据hash再次请求模块名.xxx.hot-update.js

086cfc2eb56c8351ea61d83974cf918b.png

至于为什么浏览器会请求后面会说明。

  1. 页面更新为最新代码效果。

随之心里也出现了很多疑惑,例如编译打包的文件存哪里了?为什么可以触发重新编译,通过什么知道改变的?浏览器什么时候发的请求?浏览器如何做到不刷新页面就可以更新最新效果的?有些问题很难搜到想要的答案,只能带着这些疑问从源码找到说服自己的答案。

先来看第一个问题:打包的文件存哪里了?

存到内存中了,如何存的?是通过webpack-dev-middleware的"memory-fs"库将文件写入了内存中而不是硬盘,好处就是访问速度比较快。

再来看下第二个问题:为什么可以触发重新编译,通过什么知道改变的?

热更新模块需要借助webpack-dev-server,当执行webpack-dev-server命令时,它启动了webpack,生成了compiler实例。然而compiler提供了监听文件变化和编译的功能,因此监听文件变化和触发编译主要是利用了compiler的watch和compile功能。

针对如何知道发生改变了是通过文件系统的文件修改时间来判断的。

继续看下第三个问题:浏览器什么时候发的请求?

简单来说当检测文件改变并完成编译时,本地服务会给客户端(浏览器)发送请求,浏览器收到消息后开始发请求。这里有引出两个问题:

  1. 本地服务是怎么启动的?

本地服务其实是webpack-dev-server利用express创建的服务,可以用来访问静态资源。启动本地服务前,调用了updateCompiler(this.compiler)方法获取websocket客户端代码路径和根据配置获取webpack热更新代码路径,修改了config的entry入口配置,将webpack-dev-server的client和webpack/hot/dev-server的代码注入到打包文件中(注入代码让浏览器可以接收消息,后面问题会涉及到)。

  1. 服务端怎么可以主动发请求到客户端(双端通信,大概猜到用了socket)

webpack-dev-server本地启动服务后通过new net.Socket()建立了socket服务,用来通知浏览器更新检查。当检测文件改变编译完成时,本地服务会给浏览器发送请求。

为什么浏览器可以接收消息,是因为启动本地服务亲前改变了entry入口,注入了socket的client代码(webpack-dev-server/client/index.js)。

image.png

  1. 浏览器如何做到不刷新页面就可以更新最新效果的? 真正的更新是主要是webpack内置的插件HotModuleReplacementPlugin的功能,它会给webpack的module对象增加hot属性,hot有很多方法例如check。

2594c9d63e78949b27f2f279249c5b1d.png 浏览器接收socket后发起ajax请求xx.hot-update.json请求得到最新的模块和hash值

086cfc2eb56c8351ea61d83974cf918b.png 然后根据hash值通过jsonp方式请求模块.hash.hot-update.js,得到一个webpackHotUpdate函数。

4f924958cb3d37ac4082ff0465a213a7.png

然后将最新的内容通过script标签插入到head中,如图所示:

a2fb8e9264281395b113f46e154f869e.png

启动本地服务前改变entry入口,注入了检查更新代码(webpack/hot/dev-server),会增加webpackHotUpdate的监听,当调用该函数时继续调用hotAddUpdateChunk方法会把更新的模块赋值给全局变量hotUpdate。

hotUpdateDownloaded方法会调用hotApply进行代码的替换:包含旧模板的替换、新模块的加入和通过__webpack_require__执行模块。

通过以上几个问题,相信对于热更新有了更深的了解,想要真正掌握热更新需要对webpack有比较深入的理解才行。没深入学习webpack之前,以为自己什么都知道,深入学习后不得不保持敬畏心。

因此当自己觉得无所不知的时候,说明最近没有成长,提示我们该学习了,再次印证了那句话:已知圈越大,未知圈越大

最近学习webpack整理的文章,感谢点赞支持~

【webpack系列】从源码角度分析webpack打包产出及核心流程

【webpack系列】从源码角度分析loader是如何触发和执行的

【webpack系列】webpack是如何解析模块的

【webpack系列】webpack的plugin插件是如何运行的