微前端场景聊聊资源缓存问题

667 阅读9分钟

跟普通用户聊起缓存,一般首先会联想到手机和电脑的缓存,而且是个相对不好的东西,经常弄个某某安全管家,阶段性清理下,才会心安。尤其让我印象深刻的,是早期Windows95 98的年代,一进桌面,疯狂右键点刷新,等到刷新到界面很流畅的时候才会停下来去干别的事。是不是说的就是你?

在开发者眼里,缓存的场景就会有很多。我们先来看下,维基百科对其的定义

原始意义是指访问速度比一般随机存取存储器(RAM)快的一种RAM,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。

相信学过计算机组成原理的同学再去翻翻自己的课本,也会见到相类似的定义。当然随着时间的迁移,缓存的概念不仅仅指的是SRAM或者CPU内部的一级、二级乃至三级缓存,有个更加广泛的使用

如今缓存的概念已被扩充,不仅在CPU和主内存之间有Cache,而且在内存和硬盘之间也有Cache(磁盘缓存),乃至在硬盘与网络之间也有某种意义上的Cache──称为Internet临时文件夹网络内容缓存等。凡是位于速度相差较大的两种硬件之间,用于协调两者数据传输速度差异的结构,均可称之为Cache。

我们今天要聊的缓存,就指的是浏览器的缓存,主要与服务器资源文件之间的问题,以及如何运用。

笔者所在的团队所使用的微前端,从概念上来说的确和完整的微前端之间有些差距,并没有像qiankun.js或者其他方案一样,使用spa或者WebCompent等实现css隔离,js隔离等。主要原因并不是对那些技术实现有迟疑或者技术水平问题,更多的是在考虑合理和可靠的迁移路径。因为在任何一个公司的技术团队,都要考虑投入产出比,还有风险问题,这是相纯技术的升级和迁移,并不实际给用户带来业务功能和实际体验上的提升,因此我们还是用着最原始的微前端方案,什么叫原始,原始到什么地步,有兴趣可以加我微信聊聊,哈哈。

当前笔者团队的问题主要来自于,每当用户切换前端应用,就会完整地从服务器拉取一份资源文件,怎么保证一定是最新的呢?有个大聪明在一早就这么实现的,资源请求url后面加?{Date.now()},是不是也有同学是这么干的。这当然也是个解法,而且我们团队一直用了很久。

虽然保证了资源一定是最新的但是实际给用户带来的体验就是在切换应用的时候,资源加载时间过长,实际感官就是白屏时间有点长,虽然To B应用只是内部用户,但是还是值得投入思考和分析的。

于是乎我们看看,能否有一个简单有效的方案,在保证应用升级后一定能够加载到最新的资源,在应用未升级的时候还是能够利用浏览器的缓存去快速加载的问题。

理论基础

  1. HTTP 请求缓存存在于相同URL的get请求,非get请求(idempotent request)在默认规范不存在缓存,当然也可以特殊处理

2.在HTTP get请求url后面添加随机字符串或者动态时间可以避免资源被浏览器缓存

3.http缓存根据是否需要重新向服务器发起请求来分类,如图所示

4.浏览器缓存根据存放位置可以分为内存缓存和磁盘缓存,这个不展开论述

5.其他更多细节的http缓存及http协议本身的知识可以参考w3c相关规范或者《图解HTTP》

前端编译的目标

1.资源加载和转化,webpack的loader+plugin,loader用来支持更多的文件类型,比如jsx,css,ts,json,jpg,plugin用来做更多的转换和预处理,包括less转成css,根据目标浏览器配置做兼容性处理,js语言版本的问题

2.还有polyfill(垫片)的问题,比如es6 es7编译到es5 ,新语法问题可以通过ast编译转化后降级,但是新增的api是没有办法

解决的,比如Array.filter,Promise ,这种就通过polyfill填充不足的api,但是也需要考虑目标浏览器版本,否则polyfill代码部分就会非常大反而影响加载速度

3.按需编译,拆分,压缩混淆等等。其他的不过多赘述参考webpack5文档

4.这里有个非常重要的点,webpack在打包过程中会生成manifest.json,此文件详细记录了入口文件地址,因此可以依赖这份信息,进行入口动态文件名的获取,为方案优化提供了可能(其他打包工具也一样会生成,这是个行业共识)

这里有几个注意点

  1. 通常情况下GET请求会产生缓存,那么我们请求manifest.json的时候可以通过添加?xxxxx动态后缀的方式,也可以使用POST去请求来解决
  2. 建议给所有的资源文件在编译的时候都加上hash后缀
  3. 因为当前团队的微前端应用超过了30个,有部分老应用常年使用但是没有更新,迭代过程可能会有遗漏,笔者团队实现了从打包工具到技术组件等全部自研接管的目标,因此老应用在打包工具是否升级的区别就在于是否开启了manifest.json,这里大家不要误解,因此笔者为了防止遗漏,在门户的代码侧依然兼容了老应用的加载方式,也就是强制拿最新资源文件的方式

版本号管理方案

笔者的团队对前端资源文件实现了Tag号的版本管理,但是在部署形态上并没有区分版本,也就是在Nginx资源文件代理上,并没有实现类似/view/web/app.name/{app.name}/{app.version}/xxx的概念

参考在老东家的时候实现的方案,如果在部署形态上有前端资源版本的概念,那么这个缓存的问题的解决方案就是另外一个形态。简而言之,就是在门户侧实现有前端应用管理模块,实现应用注册,资源地址配置和版本号管理等,在门户加载微前端应用的时候再动态获取当前微前端资源和版本号信息进行动态加载,那么就可以不去采用这么一种实现方案,因为版本号就能区分。随之而来的一个问题就是CI/CD,已经版本发布上的一个成本就是要在版本发布后更新这份应用注册表。这一点仁者见仁智者见智,还是一句话看团队的实际场景和诉求,投入产出比和风险一定是个绕不开的话题。

写在最后

2022年是个不平凡的一年,是不是大家都已经是“杨过”,或者“小阳人”的状态,还希望大家注意身体。22年的软件圈,互联网圈都在经历大裁员,行业寒冬,经常逛掘金和朋友圈,发现不少同学也在经历,比起身体上被病毒的折磨,相信工作和事业上的打击才是最沉痛的,祝愿大家能够度过难关。

笔者中间也断更了很久,有忙的原因,也有迷惘的因素。年底了,述职,复盘,回溯这一年,展望下一年的时候,感受着天气和行业的寒气,的确让人不寒而栗,肩上的担子,心中的抱负,思想的枷锁,都得一一闯关。

笔者会在自己和大家的鼓励中一起继续努力。对技术有共同爱好的同学可以关注我的公粽号:喵爸的小作坊

送大家一段歌词,《起风了

这一路上走走停停

顺着少年漂流的痕迹

迈出车站的前一刻

竟有些犹豫

不禁笑这近乡情怯

仍无可避免

而长野的天

依旧那么暖

风吹起了从前

从前初识这世间

万般流连

看着天边似在眼前

也甘愿赴汤蹈火去走它一遍

如今走过这世间

万般流连

翻过岁月不同侧脸

措不及防闯入你的笑颜

我曾难自拔于世界之大

也沉溺于其中梦话

不得真假 不做挣扎 不惧笑话

我曾将青春翻涌成她

也曾指尖弹出盛夏

心之所动 且就随缘去吧

逆着光行走 任风吹雨打

短短的路走走停停

也有了几分的距离

不知抚摸的是故事 还是段心情

也许期待的不过是 与时间为敌

再次看到你

微凉晨光里

笑得很甜蜜

从前初识这世间

万般流连

看着天边似在眼前

也甘愿赴汤蹈火去走它一遍

如今走过这世间

万般流连

翻过岁月不同侧脸

措不及防闯入你的笑颜

我曾难自拔于世界之大

也沉溺于其中梦话

不得真假 不做挣扎 不惧笑话

我曾将青春翻涌成她

也曾指尖弹出盛夏

心之所动 且就随缘去吧

晚风吹起你鬓间的白发

抚平回忆留下的疤

你的眼中 明暗交杂 一笑生花

暮色遮住你蹒跚的步伐

走进床头藏起的画

画中的你 低着头说话

我仍感叹于世界之大

也沉醉于儿时情话

不剩真假 不做挣扎 无谓笑话

我终将青春还给了她

连同指尖弹出的盛夏

心之所动 就随风去了

以爱之名 你还愿意吗