索引:
- Fetch网络请求数据
- RN数据存储技术
- RN离线缓存框架
Fetch网络请求数据
一个Fetch简单的使用,请求一个github的api然后显示收到的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
import React, {Component} from 'react'; import {View, Text, StyleSheet, Button, TextInput} from 'react-native'; import actions from '../action'; import {connect} from 'react-redux'; export default class FavoritePage extends Component{ constructor(props) { super(props); this.state = { showText:'', }; } loadData = () =>{ // https://api.github.com/search/repositories let url = `https://api.github.com/search/repositories?q=${this.searchKey}`; fetch(url) .then(response=>response.text()) .then(responseText => { this.setState({ showText: responseText, }); }); }; loadData2 = () =>{ // https://api.github.com/search/repositories let url = `https://api.github.com/search/repositories?q=${this.searchKey}`; fetch(url) .then(response=>{ if (response.ok){ return response.text(); } throw new Error('Network response fail'); }) .then(responseText => { this.setState({ showText: responseText, }); }) .catch(e => { this.setState({ showText: e.toString(), }); }); }; render(){ return ( <View style={styles.container}> <Text style={styles.welcome}>Fetch的使用</Text> <View style={styles.input_container}> <TextInput style={styles.input} onChangeText={text => { this.searchKey = text; }} /> <Button title={'获取'} onPress={ ()=>{ this.loadData2(); } } /> </View> <Text> {this.state.showText} </Text> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, // justifyContent: 'center', // alignItems:'center', backgroundColor: '#F5FCFF', }, welcome:{ fontSize: 20, textAlign: 'center', margin: 10, }, input:{ height: 30, flex: 1, borderColor: 'black', borderWidth: 1, marginRight:10, }, input_container:{ flexDirection: 'row', alignItems: 'center', }, }); |
数据存储技术
通常在RN中是通过AsyncStorage机型持久化的key-value存储系统,取代LocalStorage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
import React, {Component} from 'react'; import {View, Text, StyleSheet, Button, TextInput, AsyncStorage} from 'react-native'; type Props={} const KEY = 'save_key'; export default class AsyncStorageDemoPage extends Component{ constructor(props) { super(props); this.state = { showText:'', }; } /** * 存储数据 */ doSave(){ // 用法一 AsyncStorage.setItem(KEY, this.value, error => { error && console.log(error.toString()); }); } /** * 移除数据 */ doRemove(){ AsyncStorage.removeItem(KEY, error => { error && console.log(error.toString()); }); } /** * 获取数据 */ getData(){ AsyncStorage.getItem(KEY, (error, value) => { this.setState({ showText: value, }); console.log(value); error && console.log(error.toString()); }); } render(){ return ( <View style={styles.container}> <Text style={styles.welcome}>AsyncStorage的使用</Text> <TextInput style={styles.input} onChangeText={text => { this.value = text; }} /> <View style={styles.input_container}> <Text onPress={()=>{ this.doSave(); }}> 存储 </Text> <Text onPress={()=>{ this.doRemove(); }}> 删除 </Text> <Text onPress={()=>{ this.getData(); }}> 获取 </Text> </View> <Text> {this.state.showText} </Text> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F5FCFF', }, welcome:{ fontSize: 20, textAlign: 'center', margin: 10, }, input:{ height: 30, borderColor: 'black', borderWidth: 1, marginRight:10, }, input_container:{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-around', }, }); |
离线缓存框架
- 提升用户体验:我们要为用户提供流程的APP操作体验,但是我们无法保证所有用户的网络流畅度都是好的,所以我们需要离线缓存来提升用户体验
- 节省流量:节省服务器流量和手机流量
离线缓存的策略:
三种策略:
- 优先从本地获取数据,如果数据过时或者不存在则从服务器进行获取,数据返回后同时将数据同步到本地的数据库。
- 优先从服务器获取数据,数据返回后的同时将数据同步到本地数据库,如果网络故障则从本地获取数据。
- 同时从本地和服务器获取数据,如果本地数据库返回数据则先展示本地数据,等网络数据回来后再展示网络数据的同时将数据同步到本地数据库。
在该框架中将使用第一种策略。
框架的核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
import {AsyncStorage} from 'react-native'; export default class DataStore { /** * 获取数据,优先获取本地数据,如果无本地数据或本地数据过期则获取网络数据 * @param url * @returns {Promise} */ 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); })); }); }); } /** * 保存数据 * @param url * @param data * @param callback */ saveData(url, data, callback) { if (!data || !url) {return;} AsyncStorage.setItem(url, JSON.stringify(this._wrapData(data)), callback); } /** * 获取本地数据 * @param url * @returns {Promise} */ 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); } }); }); } /** * 获取网络数据 * @param url * @returns {Promise} */ 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(url, responseData); resolve(responseData); }) .catch((error) => { reject(error); }); }); } _wrapData(data) { return {data: data, timestamp: new Date().getTime()}; } /** * 检查timestamp是否在有效期内 * @param timestamp 项目更新时间 * @return {boolean} true 不需要更新,false需要更新 */ 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;}//有效期4个小时 // if (currentDate.getMinutes() - targetDate.getMinutes() > 1)return false; return true; } } |
然后创建一个页面来测试这个框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
import React, {Component} from 'react'; import {StyleSheet, Text, TextInput, View} from 'react-native'; import DataStore from '../expand/dao/DataStore'; type Props = {}; const KEY = 'save_key'; export default class DataStoreDemoPage extends Component<Props> { constructor(props) { super(props); this.state = { showText: '', }; this.dataDtore = new DataStore(); } loadData() { let url = `https://api.github.com/search/repositories?q=${this.value}`; this.dataDtore.fetchData(url) .then(data => { let showData = `初次数据加载时间:${new Date(data.timestamp)}\n${JSON.stringify(data.data)}`; this.setState({ showText: showData, }); }) .catch(error => { error && console.log(error.toString()); }); } render() { return ( <View style={styles.container}> <Text style={styles.welcome}>离线缓存框架设计</Text> <TextInput style={styles.input} onChangeText={text => { this.value = text; }} /> <Text onPress={() => { this.loadData(); }}> 获取 </Text> <Text> {this.state.showText} </Text> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, input: { height: 30, borderColor: 'black', borderWidth: 1, marginRight: 10, }, input_container: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-around', }, }); |