Published on

vite-1.0 serverPluginHmr文件解读

Authors
  • avatar
    Name
    祝你好运
    Twitter

超快的HMR也是Vite的卖点之一,HMR的功能是通过客户端和这个服务端的插件来共同完成的。

主结构

简单来说就是一个koa中间件,当HMR客户端发来upgrade消息的时候,服务端升级为WebSockets(第81行),然后附加一些打log和错误处理(第89行和94行)。另外就是当服务端有JS文件变更的时候,调用handleJSReload来发送消息到客户端,客户端负责更新到页面上面,这样就完成了HMR。

hmr-plugin-main-structure

WebSockets

这里也简单提一下WebSockets,WebSockets就是Web上面的Socket,我们知道Socket里面客户端和服务端都可以发送消息,Web上面的HTTP协议,只能客户端拉取,无法做到服务端推送(除了HTTP/2)。那WebSockets就是为了弥补HTTP的这一缺陷而存在的。

WebSockets的RFC在这里,它和HTTP一样是基于TCP的应用层协议,建立过程是先3次握手建立TCP连接,然后由客户端发送一个带有Connection: upgradeheader的HTTP请求,然后服务端返回HTTP code 101,表示切换协议,然后就转到WebSockets,后续就直接走WebSockets,跟HTTP就没关系了。

hmr-websocket-connect-switch-request
对应的HTTP响应如下图: hmr-websocket-connect-switch-response 注意紧接着红色箭头处出现的WebSocket是vite发的{"type":"connected"},这个不是标准流程里面的。

我这里的截图是用的Wireshark,选择的网卡是Loopback,因为我本地打开的就是http://localhost:3000

handleJSReload

这个函数的主结构如下图。首先是要处理Vue,因为Vue里面JS是可以通过.vue文件编译出来的,所以这里会有个特殊处理。然后如果有文件导入用户修改的这个文件,或者是有文件通过HMR接受用户修改的这个文件,就可以走到125行的if里面。否则就不需要发送HMR到客户端,直接打个log结束。

hmr-plugin-handleJSReload-main-structure

具体到当一个文件被修改之后,客户端浏览器应该怎么更新,可以分为两大类。如果没法做HMR(130行的hasDeadEnd)就全量更新,否则就把需要更新的模块发送给客户端。这个hasDeadEnd是一个递归判断的,有两种情况为true,一种是文件中明确拒绝HMR(189行),另一种就是它的父级(也就是网上递归查找)都不支持HMR(会走到218行)。

hmr-plugin-update-main-structure

具体哪些文件需要更新,是记录在hmrBoundaries里面的,这个也是walkImportChain里面记录的,就是递归找到的所有父级中支持HMR的文件。

rewriteFileWithHMR