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;
}
以上就是整個緩存策略的實現