1.为什么要离线缓存?
宏观上来说:
提升用户体验: 我们要为用户提供流畅的APP操作体验,但我们无法保证所有用户的网络流畅度是好的,所以我们需要离线缓存来提升用户体验。
节省流量: 节省流量又分为两个层次:
- 节省服务器流量
- 节省用户手机的流量
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, 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的策略: 优先从本地获取数据,如果数据过时或不存在则从服务器获取数据,我们需要这样设计我们的代码:
- 我们优先从本地获取数据
- 如果数据存在且在有效期内,我们将数据返回
- 否则我们获取网络数据
在上述代码中,我们通过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; }以上就是整个缓存策略的实现