vue中多个相同组件重复请求的问题?

image.png
image.png
现在因为有个上传组件他用到了这个mixin文件,这个mixin文件中mounted请求了接口,这样一个页面中如果有多个上传组件的话,那么就会导致一上来就会一次性请求多个重复的接口,有什么好点的方法吗,如果不放在vuex中的话

阅读 8.8k
8 个回答

这种情况下的 getSignature 最好是 Singleton 的,可以写个模块导出。mixin 里的 getSignatrue 都直接引用或调用这个 Singleton 的 getSignature。

因为大家都调同一个 Signature,那么可以在这里做一些判断和处理,基本思路就是,

  • 有缓存拿缓存
  • 没缓存判断如果是第 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;
}

我遇到了类似的问题,我的场景是一个图片加载组件在mounted的时候会根据props的图片id去请求图片的url,在列表中展示时,会请求很多次。这个接口是支持单个图片id作为参数 同时也支持id数组作为参数的,也就是说我可以一次请求多个图片id,当调用该接口时,我将短时间内的请求参数进行收集然后合并为一个请求,拿到结果后再进行分发。公司代码不便展示 我这里写了个demo可以参考一下

let isCollecting = false
const range = 200
const stack = []
// 模拟http请求
const fetchData = (params) => {
    return new Promise((resolve) => {
        setTimeout(() => {
            if (Array.isArray(params)) {
                const list = params.map(item => {
                    return {
                        id: item.id,
                        url: 'url:' + item.id
                    }
                })
                resolve(list)
            } else {
                resolve(
                    {
                        id: params.id,
                        url: "url:" + params.id
                    }
                )
            }

        }, 50)
    })
}
// 收集请求
const mergeRequest = (params) => {
    return new Promise((resolve) => {
        if (!isCollecting) {
            console.log('开始收集')
            isCollecting = true
            if (Array.isArray(params)) {
                stack.push({
                    params, resolve
                })
            } else {
                stack.push({
                    params, resolve
                })
            }
            console.log('创建定时器 200ms后清空stack')
            setTimeout(async () => {
                await clearStack()
                console.log('清空stack')
                isCollecting = false
            }, range)
        } else {
            console.log('收集')
            stack.push({
                params, resolve
            })
        }
    })
}
// 合并请求参数,发起请求,处理结果
const clearStack = async () => {
    const paramsList = stack.reduce((acc, cur) => {
        if (Array.isArray(cur.params)) {
            acc.push(...cur.params)
        } else {
            acc.push(cur.params)
        }
        return acc
    }, [])

    const res = await fetchData(paramsList)
    stack.forEach(item => {
        const { params, resolve } = item
        const obj = {

        }
        if (Array.isArray(params)) {

            obj.params = params
            obj.result = params.map(v => {
                return res.find(tar => tar.id === v.id)
            })
            resolve(obj)
        } else {
            obj.params = params
            obj.result = res.find(tar => tar.id === params.id)
            resolve(obj)
        }
    })
    console.log('clearSatck')
    stack.length = 0

}
// 延时函数
const sleep = async (num) => {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve()
        }, num);
    })
}
//
const fn = async () => {
    mergeRequest({ id: 1 }).then(res => {
        console.log(res)
    })
    await sleep(100)
    mergeRequest([{ id: 2 }, { id: 3 }, { id: 4 }]).then(res => {
        console.log(res)
    })
    await sleep(100)
    mergeRequest({ id: 5 }).then(res => {
        console.log(res)
    })
    await sleep(100)
    mergeRequest({ id: 6 }).then(res => {
        console.log(res)
    })
}
fn()

给组件加一个属性来决定是否需要调用方法。
多个组件一起使用的时候,直接全局mixin

你可以全局调用 getSignature() 一次把结果存起来,组件里直接拿结果

或给你的 getSignature() 加个缓存机制,如

let signature = null
const getSignature = () => signature ??= httpGet('...')

给请求加下缓存 思路如下:
定义一个数组 const a = []
然后每次请求给url push 到数组里 作为唯一值
请求完毕弹出对应的url
如果下次请求判断里面有这个url 那么就不再进行请求

那这样肯定不能把getSignature放到mixin了,因为你要保持只请求一次,不然处理起来有点复杂,就没太大必要了。

可以在Appmounted就调用一次。
如果你不想使用vuex,那可以通过sessionStorage来获取。
也可以通过把数据写到vueprototype中,这样在用到的地方就可以通过this.$xxx来获取。

对于这种请求重复请求 还有一个思路 做下防抖节流

比如1秒内只能发出一次这种请求

简单粗暴版:
在初始化时请求接口并保存,然后 getSignature 中直接读取该数据即可,伪代码:

app.js

global.sign = await api();
app.start();

mixin.js

{getSign: () => global.sign}

正常版本:

let cache = null;
async getSignature = () => {
    if(cache) return cache;
    return await api();
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题