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;
    }

以上就是整個緩存策略的實現


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM