问题引入
在页面开发的时候, 如果一个页面中有多个相同的组件, 那么该组件每次onMounted的时候都会去调用对应的api,就会造成类似这样的后果多次请求了同一个api
如何解决?
方式一
借助一个大佬的思路使用单例模式
基本思路
- 有缓存拿缓存
- 没缓存判断如果是第 1 个请求的,就去请求远端
- 如果不是第 1 个请求的,就等
let cache = null;
let count = 0;
async function delay(ms = 200) {
return new Promise(resolve => setTimeout(resolve, ms));
}
export async function getSignature() {
if (cache) { return cache; }
if (count++) {
// 如果有计数说明自己不是第 1 个,就等。注意这里判断的是加之前的 count
// 循环里最好再加个超时判断
while (!cache) { await delay(); }
} else {
// 是第 1 个就去请求
// 如果这里有可能会抛异常,抛异常也不要漏了 count--
// (这个示例代码没做容错,自己加)
cache = await fetchSignature();
}
count--; // 记得减回去,方便以后如果要刷新 cache 的时候用
return cache;
}
多个组件调用 getSignature() , 如果是第一次请求,走的是 if(count++) ,此时if判断语句里面的 count 值还是 0 , 走出if 语句之后才是 1 , 所以会进入 while(!cache) 语句, 进行循环等待, 到了第二个组件请求的时候, count 的值 已经是 1 了,那么这个时候 走的就是else 语句, 就可以获得cache结果,此时 第一次请求的条件 !cache也不成立了, 就会退出循环等待, 就会返回 cache的请求结果
关于 count-- : 由于首次和后续的29次请求的时候都会执行count++ 这个语句, 那么每次的count 结果都会加1 . 结束后都需要把count减一次,不需要放在else中。这样下来30个请求结束后,count置为0了。如果放在else中,那么30次请求结束后,count的值应该是1.
方式二
同样使用的是单例模式, 不过这里是异步单例模式 关于异步单例模式,我觉得可以参考这篇文章高级异步模式 - Promise 单例
实践一波
function getSomething() {
return new Promise(resolve => {
const result = fetch('http://hn.algolia.com/api/v1/search?query=vue').then(val => resolve(val.json()))
})
}
let myfetch = null
async function getData() {
if (myfetch) return myfetch
myfetch = getSomething()
console.log(345);
try {
const result = await myfetch // 这里一定要 await myfetch
console.log(456);
myfetch = null
return result
} catch (err) {
console.err('err')
}
}
const result1 = getData()
const result2 = getData()
const result3 = getData()
const result4 = getData()
console.log(123);
console.log(result1, 'result1'); // Promise {<pending>} 'result1'
console.log(result2, 'result2'); // Promise {<pending>} 'result2'
console.log(result3, 'result3'); // Promise {<pending>} 'result3'
console.log(result4, 'result4'); // Promise {<pending>} 'result4'
setTimeout(() => {
console.log(result1, 'result1'); // Promise {<fulfilled>: {…}} 'result1'
console.log(result2, 'result2'); // Promise {<fulfilled>: {…}} 'result2'
console.log(result3, 'result3'); // Promise {<fulfilled>: {…}} 'result3'
console.log(result4, 'result4'); // Promise {<fulfilled>: {…}} 'result4'
}, 1500)
第一次调用的时候, 没有myfetch , 就去 调用 接口, 此时的 myfetch 是pending 状态,因为是多个组件同时调用getData()函数, 但是我们再第一次的时候已经给myfetch赋值了,所以调用getData()函数返回的都是pending 状态的myfetch , 等到第一个请求的结果拿到了, 那么其他组件也就拿到了这个结果,所以就不用每个组件都去走一次http请求拿到同样的结果,这样下来就可以不用每个组件都去调用接口