在开发 web 应用程序时,性能是一个重要的话题。为了提高用户体验和节省网络资源,我们需要对一些频繁或重复的 API 请求进行缓存,以减少不必要的服务器交互。缓存的原理是将第一次请求的数据保存在客户端,之后再次请求时直接从缓存中获取,而不是向服务器发送请求。

缓存的方式有很多种,根据不同的业务场景和需求,我们可以选择合适的方案来实现缓存功能。本文将介绍前端 API 请求缓存的三种方案,从简单到复杂,分别是:

一、数据缓存

  • 数据缓存是最简单的一种缓存方案,它的思路是将第一次请求的数据保存在一个 Map 对象中,以 API 的名称或参数作为键,以数据作为值。之后再次请求时,先从 Map 对象中查找是否有对应的数据,如果有则直接返回,如果没有则向服务器发送请求,并将返回的数据保存在 Map 对象中。
  • 数据缓存的优点是实现简单,适用于一些不需要更新的静态数据。缺点是无法处理同时多次请求的情况,也无法设置缓存的有效期,可能会导致数据过时或冗余。

示例代码如下:

// 创建一个 Map 对象用于存储数据
const dataCache = new Map();

// 定义一个获取商品列表的 API
async function getWares() {
    // 以 API 的名称作为键
    let key = "wares";
    // 从 Map 对象中获取数据
    let data = dataCache.getkey;
    if (!data) {
        // 如果没有数据,向服务器发送请求
        const res = await request.get("/getWares");
        // 对返回的数据进行一些处理
        data = res.data;
        // 将数据保存在 Map 对象中
        dataCache.set(key, data);
    }
    // 返回数据
    return data;
}

调用方式如下:

// 第一次调用,向服务器发送请求,获取数据
getWares().then((data) => {
    console.log(data);
});
// 第二次调用,从 Map 对象中获取数据,不再发送请求
getWares().then((data) => {
    console.log(data);
});

二、Promise 缓存

  • Promise 缓存是对数据缓存的改进,它的思路是将第一次请求的 Promise 对象保存在一个 Map 对象中,以 API 的名称或参数作为键,以 Promise 对象作为值。之后再次请求时,先从 Map 对象中查找是否有对应的 Promise 对象,如果有则直接返回,如果没有则向服务器发送请求,并将返回的 Promise 对象保存在 Map 对象中。
  • Promise 缓存的优点是可以处理同时多次请求的情况,避免重复发送请求,只有第一次请求会向服务器发送请求,之后的请求都会等待第一次请求的结果。缺点是无法设置缓存的有效期,可能会导致数据过时或冗余。

示例代码如下:

// 创建一个 Map 对象用于存储 Promise 对象
const promiseCache = new Map();

// 定义一个获取商品列表的 API
function getWares() {
    // 以 API 的名称作为键
    let key = "wares";
    // 从 Map 对象中获取 Promise 对象
    let promise = promiseCache.getkey;
    // 如果没有 Promise 对象
    if (!promise) {
        // 向服务器发送请求,并返回一个 Promise 对象
        promise = request.get("/getWares").then((res) => {
            // 对返回的数据进行一些处理
            return res.data;
        }).catch((error) => {
            // 如果请求出错,从 Map 对象中删除 Promise 对象,避免缓存错误的结果
            promiseCache.deletekey;
            // 抛出错误
            return Promise.reject(error);
        });
        // 将 Promise 对象保存在 Map 对象中
        promiseCache.set(key, promise);
    }
    // 返回 Promise 对象
    return promise;
}

调用方式如下:

// 第一次调用,向服务器发送请求,获取数据
getWares().then((data) => {
    console.log(data);
});
// 第二次调用,从 Map 对象中获取 Promise 对象,不再发送请求,等待第一次请求的结果
getWares().then((data) => {
    console.log(data);
});

三、多 Promise 缓存

  • 多 Promise 缓存是对 Promise 缓存的扩展,它的思路是将多个 API 请求的 Promise 对象保存在一个 Map 对象中,以 API 的名称或参数作为键,以 Promise 对象作为值。之后再次请求时,先从 Map 对象中查找是否有对应的 Promise 对象,如果有则直接返回,如果没有则向服务器发送请求,并将返回的 Promise 对象保存在 Map 对象中。最后,使用 Promise.all 方法将多个 Promise 对象合并为一个 Promise 对象,统一返回结果。
  • 多 Promise 缓存的优点是可以同时获取多个 API 请求的数据,提高效率,只有第一次请求会向服务器发送请求,之后的请求都会等待第一次请求的结果。缺点是无法设置缓存的有效期,可能会导致数据过时或冗余。另外,如果其中一个 API 请求出错,会导致整个 Promise 对象被拒绝,无法获取其他 API 请求的数据。

示例代码如下:

// 定义一个对象,存储 API 的名称和对应的请求地址
const querys = {
    wares: "/getWares",
    skus: "/getSku",
};

// 创建一个 Map 对象用于存储 Promise 对象
const promiseCache = new Map();

// 定义一个函数,用于同时获取多个 API 请求的数据
async function queryAll(queryApiName) {
    // 判断传入的参数是否是数组
    const queryIsArray = Array.isArray(queryApiName);
    // 统一处理参数,无论是字符串还是数组,都视为数组
    const apis = queryIsArray ? queryApiName : [queryApiName];
    // 创建一个数组,用于存储所有的 Promise 对象
    const promiseApi = [];
    // 遍历参数数组,对每个 API 进行处理
    apis.forEach((api) => {
        // 从 Map 对象中获取 Promise 对象
        let promise = promiseCache.get(api);
        // 如果没有 Promise 对象
        if (!promise) {
            // 向服务器发送请求,并返回一个 Promise 对象
            promise = request.get(querys[api]).then((res) => {
                // 对返回的数据进行一些处理
                return res.data;
            }).catch((error) => {
                // 如果请求出错,从 Map 对象中删除 Promise 对象,避免缓存错误的结果
                promiseCache.delete(api);
                // 抛出错误
                return Promise.reject(error);
            });
            // 将 Promise 对象保存在 Map 对象中
            promiseCache.set(api, promise);
        }
        // 将 Promise 对象添加到数组中
        promiseApi.push(promise);
    });
    // 使用 Promise.all 方法将多个 Promise 对象合并为一个 Promise 对象
    return Promise.all(promiseApi).then((res) => {
        // 根据传入的参数是字符串还是数组来返回数据,因为本身都是数组操作
        // 如果传入的是字符串,则需要取出第一个元素
        return queryIsArray ? res : res[0];
    });
}

调用方式如下:

// 第一次调用,向服务器发送请求,获取数据
queryAll("wares").then((data) => {
    console.log(data);
});
// 第二次调用,从 Map 对象中获取 Promise 对象,不再发送请求,等待第一次请求的结果
queryAll(["wares", "skus"]).then((data) => {
    console.log(data);
});

总结

通过实施API请求缓存,我们可以显著提高前端应用程序的性能和用户体验。选择合适的缓存方案取决于应用程序的需求和特点。在实际开发中,我们可以根据数据的变化频率、数据的重要性、存储空间的限制等因素来选择适当的方案。结合合适的缓存方案,我们可以有效地减少网络请求,提高应用程序的响应速度,并降低服务器负载。同时,需要注意缓存的更新策略,以确保数据的准确性和实时性。

如果你对前端工程师职业和编程技术感兴趣,不妨访问编程狮官网(https://www.w3cschool.cn/)。编程狮官网提供了大量的技术文章、编程教程和资源,涵盖了前端工程师、编程、职业规划等多个领域的知识。无论你是初学者还是有经验的开发者,编程狮官网都为你提供了有用的信息和资源,助你在编程领域取得成功。不要错过这个宝贵的学习机会!