之前开发过程中遇到的问题。(由于购买的是外部接口,现在已经到期了,最近秋招在复盘项目中遇到的困难,所以没办法复现,拿出截图证明数据)
首先说一下需求吧
整体是一个大屏项目,然后中间有地图使用的是LarkMap,我需要在地图上渲染出云层流动的效果(这里用的是一个input框,滑动数值,通过定时器循环切换图片,达到云层流动快慢的效果)。
然后再清点一下所拥有的资源
两个接口(第三方购买的),第一个接口携带key用于请求所有图片的url,第二个接口利用第一个接口请求回来的url携带上第一个接口请求回来的key,请求图片资源。
首次做法
通过嵌套接口调用,请求回来云层图片的url,将云层图片的url数组,通过useState存到状态中,然后通过一个函数循环请求图片,将请求结果渲染到页面上。
目前遇到的问题
通过两个接口嵌套,每张图片都通过网络请求获取,然后记忆中每张图片平均请求时间是173ms,而且不包含将图片渲染到屏幕的时间,这样导致的问题就是:云层图片切换,每次切换之前都要发送请求,然后再渲染到屏幕上,这样就会导致中间有空白期,肉眼可见的白屏。
解决思路
主要就是解决请求缓慢的问题。于是想到能否请求图片实体,将图片实体缓存到本地,每次从实体取出。
上代码:
通过async/await和Promise.all()异步请求所有图片资源,将图片资源转成blob对象,存进currentImage状态中。
const asyncFetch = async () => {
try {
const response = await fetch(
'https://something.com/v4?fields=precip_minutely_fcst_map&key=xxxxxxx',
);
const json = await response.json();
const images = [];
const times = [];
await Promise.all(
json.precip_minutely_fcst_map[0].data.slice(0, 23).map(async (item) => {
const imageUrl = `${item.url}?key=xxxxxxx`;
times.push(item.time);
const res = await fetch(imageUrl);
const blob = await res.blob();
images.push(blob);
}),
);
setCurrentImage(images);
setTimes(times);
} catch (error) {
console.error('请求出错了', error);
}
};
由于larkmap图片图层暂时支持的数据源只能是图片的url,所以还需要将blob对象转成本地url
const updateImage = () => {
//设置当前图层所对应的时间
const timeString = new Date(Times[index]).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
// 清理之前的 URL(避免内存泄漏)
if (rainSource && rainSource.data) {
window.URL.revokeObjectURL(rainSource.data);
}
let imageUrl = currentImage[index];
// 将 Blob 对象转换为可用于显示的 URL
let imgUrl = window.URL.createObjectURL(new Blob([imageUrl]));
// 更新 rainSource
setRainSource({
data: imgUrl,
parser: {
type: 'image',
extent: [63.8148899733, 12.7700338517, 143.536486117, 56.3833398551],
},
});
setCurrentImageTime(timeString);
setIndex((index + 1) % 23);
};
结果
从本地取出的图片平均耗时为4ms(之前本地缓存没消失,还能返回304),白屏问题的到解决了。