背景
无论是开发/测试/生产环境,我司前端项目都需要在相应环境下的账号中心进行登录,登录后账号中心会将jwt保存至localStorage供其他各项目取用。 前端在进行本地开发时可以选择通过注释代码或手动添加有效jwt至localStorage来绕过登录流程,但更推荐的流程是使用代理将线上域名的请求代理至本地devServer,从而保证流程的一致性。 但使用代理始终存在一个问题,就是热更新会失效,每次更新代码后需要手动刷新浏览器查看效果。
现象
使用charles配置map remote,将线上域名下的页面请求代理至本地
此时通过线上域名访问正常,打开控制台发现有报错
首次尝试
那既然有请求失败,那让它成功会不会就能热更新了?
本地devServer默认通过http协议进行交互,那么上面的报错应该是根本就找不到相应服务。那么还是借助charles解决,添加以下map remote规则:
再次刷新页面,发现错误变了
直接通过浏览器访问这个链接能看到系统认为证书(charles根证书)是有效的,但chrome却认为证书有问题
为什么chrome认为证书的common name(172.20.212.82)无效呢?
根据此回答stackoverflow.com/a/37063612
CA Browser Forum规定证书可以签发给IP地址,但必须是公共IP,而不是预留IP! *哪些是预留IP
至此我们可以确定,Chrome会因为172.16.0.0–172.31.255.255是一个内网预留的ip段而直接报ERR_CERT_COMMON_NAME_INVALID
第二次尝试
既然不能用本地ip发起这个请求,那么可以通过devServe.public修改访问本地服务的地址
并且为path为/sockjs-node的请求添加map remote规则
添加完成后刷新页面,此时页面在不断发起3个请求
第1个请求已经可以顺利代理到本地并拿到响应,但第2个请求竟是个websocket请求,还需添加相应map remote规则
第3个请求则直接返回了一个错误响应,想必这就是页面不断在轮询的原因
可以通过添加devServer.disableHostCheck = true
绕过对请求发起域名的检查
热更新原理
看到这里你可能对热更新的工作原理产生了一点似是而非的感觉。 让我们从头梳理一下。
- 起初devServer返回的前端页面会在后台向
/sockjs-node/info?t=xxx
发起请求,尝试获取能否建立websocket链接等信息
2. 如果请求拿到成功响应,接下来会向
/sockjs-node/xxx/xxx/websocket
尝试建立websocket链接,实现全双工通信
- 如果websocket连接失败,则将前后端通信方式降级为长链接
- 当代码发生改变,开发者保存代码,devServer重新触发编译流程,webpack生成新的模块,并在生成成功后通过websocket或长链接通知前端
5. 前端通过hash请求一个json和一个js文件
json文件当中,h
会做为下一个hash,用于请求新的json和js文件
c.app = true
表示app模块会被更新
js文件当中已经包含了对webpackHotUpdate方法的调用(非常典型的jsonp),会在拿到响应后执行热更新逻辑
6. 至此更新流程告一段落
总结
我们了解了在代理以后实现devServer热更新需要至少保证:
- 对
/sockjs-node/info?t=xxx
发起的请求能拿到成功响应 - 对
/sockjs-node/xxx/xxx/websocket
发起的请求能拿到101响应(websocket协议升级成功)或
对/sockjs-node/xxx/xxx/xhr_streaming?t=xxx
的请求能拿到成功响应(长链接)
以及
- chrome会认为给预留ip段签发的证书无效
- 热更新前后端交互基本流程