• React Native之基于AsyncStorage的离线缓存框架设计


    1.为什么要离线缓存?

    宏观上来说:

    • 提升用户体验: 我们要为用户提供流畅的APP操作体验,但我们无法保证所有用户的网络流畅度是好的,所以我们需要离线缓存来提升用户体验。
    • 节省流量: 节省流量又分为两个层次:
      - 节省服务器流量
      - 节省用户手机的流量

    2.离线缓存的策略

    • a. 优先从本地获取数据,如果数据过时或不存在则从服务器获取数据,数据返回后同时将数据同步到本地数据库
    • b. 优先从服务器获取数据,数据返回后同时将数据同步到本地数据库,如果网络故障则从本地获取数据
    • c. 同时从本地和服务器获取数据,如果本地数据库返回数据则先展示本地数据,等网络数据回来后在展示网络数据同时将数据同步到本地数据库中

    3.离线缓存的实现

    首先我们需要实现对数的存储

    3.1 数据存储

        /**
         * 保存数据到本地
         *
         * @param {} url 请求地址
         * @param {} data 数据
         * @param {} callback 回掉函数
         * @returns
         * @memberof DataStore
         */
        saveData(url, data, callback) {
            if (!data || !url) return;
            AsyncStorage.setItem(url, JSON.stringify(this._wrapData(data)), callback);
        };
    

    上述代码我们实现了一个saveData方法,它接受一个url作为缓存数据的key,接受一个object的参数data作为保存的value,因为AsyncSorage是无法直接保存object的。所以我们需要将其序列化成json。

    a策略提到了数据的有效期,所以我们要给缓存的数据加个时间戳:

        /**
         * 给数据添加上时间戳
         *
         * @param {} data 数据
         * @returns
         * @memberof DataStore
         */
        _wrapData(data) {
            return {
                data: data,
                timestamp: new Date().getTime()
            }
        };
    

    注意:我们取的是本地时间作为时间戳,本地时间存在被纂改的风险,如果条件允许可以取服务器的时间作为时间戳

    3.2 获取本地数据

     /**
         * 获取本地数据
         *
         * @param {} url 请求地址
         * @returns
         * @memberof DataStore
         */
        fetchLocalData(url) {
            return new Promise((resolve, reject) => {
                AsyncStorage.getItem(url, (error, result) => {
                    if (!error) {
                        try {
                            resolve(JSON.parse(result));
                        } catch (e) {
                            reject(e);
                            console.error(e);
                        }
                    } else {
                        reject(error);
                        console.error(error);
                    }
                })
            })
        };
    

    AsyncStorage.getItem获取的数据是String类型的,以方便使用我们需要将其反序列化成Object

    3.3 获取网络数据

        /**
         * 获取网络数据
         *
         * @param {} url 请求地址
         * @returns
         * @memberof DataStore
         */
        fetchNetData(url) {
            return new Promise((resolve, reject) => {
                fetch(url)
                    .then((response) => {
                        if (response.ok) {
                            return response.json();
                        }
                        throw new Error('Network response was not ok.');
                    })
                    .then((responseData) => {
                        this.saveData(responseData);
                        resolve(responseData);
                    })
                    .catch((error) => {
                        reject(error);
                    })
            })
        }
    
    • 通过上述代码我们获取到网络数据,并对响应不成功的情况抛出了异常
    • 在获取到网络数据的同时我们将数据同步到了本地数据库

    3.4 实现缓存策略

    按照a的策略: 优先从本地获取数据,如果数据过时或不存在则从服务器获取数据,我们需要这样设计我们的代码:
    - 我们优先从本地获取数据
    - 如果数据存在且在有效期内,我们将数据返回
    - 否则我们获取网络数据

         /**
         * 获取数据,优先获取本地数据,如果无本地数据或本地数据过期则获取网络数据
         *
         * @param {} url 请求地址
         * @returns
         * @memberof DataStore
         */
        fetchData(url) {
            return new Promise((resolve, reject) => {
                this.fetchLocalData(url)
                    .then((wrapData) => {
                        if (wrapData && DataStore.checkTimestampValid(wrapData.timestamp)) {
                            resolve(wrapData);
                        } else {
                            this.fetchNetData(url)
                                .then((data) => {
                                    resolve(this._wrapData(data));
                                })
                                .catch((error) => {
                                    reject(error);
                                })
                        }
                    })
                    .catch((error) => {
                        this.fetchNetData(url)
                            .then((data) => {
                                resolve(this._wrapData(data));
                            })
                            .catch((error) => {
                                reject(error);
                            })
                    })
            })
        }
    

    在上述代码中,我们通过DataStore.checkTimestampValid来判断数据是否有效:

         /**
         * 检查timestamp是否在有效期内
         *
         * @static
         * @param {} timestamp 项目更新时间
         * @returns
         * @memberof DataStore
         */
        static checkTimestampValid(timestamp) {
            const currentDate = new Date();
            const targetDate = new Date();
            targetDate.setTime(timestamp);
            if (currentDate.getMonth() !== targetDate.getMonth()) return false;
            if (currentDate.getDate() !== targetDate.getDate()) return false;
            if (currentDate.getHours() - targetDate.getHours() > 4) return false;
            return true;
        }
    

    以上就是整个缓存策略的实现

  • 相关阅读:
    mysql基础以优化
    Mysql建立索引基础
    Mysql(1)
    SVN学习总结
    Github
    Java Eclipse断点调试
    Java设计模式图文详解
    代理模式
    Java——动态代理技术
    Spring基本概念理解
  • 原文地址:https://www.cnblogs.com/fe-linjin/p/10576738.html
Copyright © 2020-2023  润新知