1.前言:
react-native 提供了全局的fetch函数作为替代xhr的网络通讯模块。之所以这么选择是由于xhr存在以下的缺点:
1.设计上不符合责任分离原则,将输入、输出和用事件来跟踪的状态混杂在一个对象里。
2.与最近的promise以及基于生成器的异步编程模型不太搭。
需要注意的是,fetch规范与jquery.ajax主要有两种方式的不同:
- 当接收到一个代表错误的HTTP状态码的时候,从fetch返回的promise不会被标记为reject,即使该HTTP响应的状态码是404或者500,相反它会将Promise状态标记为resolve(但是会将resolve的返回值的ok属性设置为false),仅当网络故障时或请求被阻止时,才会标记为reject。
- 默认情况下,fetch不会从服务端发送或接受任何cookies,如果站点依赖于用户session,则会导致未经认证的请求(要发送cookies,必须设置
credentials
选项)。
具体api和参数配置可以参照:
1.https://segmentfault.com/a/1190000006095018
2.fetch api :https://davidwalsh.name/fetch
2. 简单的网络请求例子:
import React,{Component} from "react";
import {View ,Text,StyleSheet,TextInput,Button} from "react-native";
export default class FetchDemoPage extends Component{
constructor(props){
super(props);
this.state = {
showText:""
};
}
loadData=()=>{
const 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 was not ok.");
}).
then(responseText=>{
this.setState({
showText:responseText
});
}).
catch((error)=>{
this.setState({
showText:error.message||"未知错误"
})
})
;
}
onChangeTextHandler=(text)=>{
this.searchKey = text;
}
render(){
return (
<View style={styles.container}>
<Text>FetchDemoPage</Text>
<View style={styles.input_container}>
<TextInput style={styles.input} onChangeText={this.onChangeTextHandler}/>
<Button title="获取" onPress={this.loadData}></Button>
</View>
<Text style={styles.showText}>
{this.state.showText}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:"#fff",
justifyContent:"center",
alignItems:"center"
},
input:{
height:30,
flex:1,
borderColor:"black",
borderWidth:1,
marginRight:10
},
showText:{
flex:1,
justifyContent:"center"
},
input_container:{
flexDirection:"row"
}
});
3.AsyncStorage
AsyncStorage是RN官方推荐的本地持久化方式,它是一个简单的、异步的、持久化的Key-Value存储系统,它对于App来说是全局性的。它用来代替LocalStorage。这是官网上对它的介绍。可以知道,这个asyncstorage也是以键值对的形式进行存储数据的。
AsyncStorage在iOS下存储分两种情况:
- 如果储存内容较小,那么会将要储存的内容放在一个序列化的字典中。
- 如果存储的值较大,那么会将存储的内容放在一个单独的文件中。底层会把数据保存到沙盒中的Documents中,并生成manifest.json文件。保存的数据都在manifest.json中。
AsyncStorage在Android下的存储也分两种情况:
- AsyncStorage会将数据存储在RocksDB或者SQLite,具体存储在哪里这取决与设备支持情况。
下面是个小例子:
import React,{Component} from "react";
import {View ,Text,StyleSheet,TextInput,Button,AsyncStorage} from "react-native";
const AS_KEY = "AS_KEY";
export default class AsyncStorageDemo extends Component{
constructor(props){
super(props);
this.state = {
showText:""
};
}
onChangeTextHandler=(text)=>{
this.searchKey = text;
}
//增加数据
onSave=()=>{
const {searchKey} = this;
AsyncStorage.setItem(AS_KEY,searchKey).catch((error)=>{
error&&console.log(error.toString());
});
}
//获取数据
onGet=()=>{
const {searchKey} = this;
AsyncStorage.getItem(AS_KEY).
then((value)=>{
this.setState({
showText:value
});
}).
catch((error)=>{
error&&console.log(error.toString());
});
}
//删除数据
onDelete=()=>{
const {searchKey} = this;
AsyncStorage.removeItem(AS_KEY).catch((error)=>{
error&&console.log(error.toString());
});
}
render(){
return (
<View style={styles.container}>
<Text>FetchDemoPage</Text>
<View style={styles.input_container}>
<TextInput style={styles.input} onChangeText={this.onChangeTextHandler}/>
<Button title="存储" onPress={this.onSave}></Button>
<Button title="获取" onPress={this.onGet}></Button>
<Button title="删除" onPress={this.onDelete}></Button>
</View>
<Text style={styles.showText}>
{this.state.showText}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:"#fff",
justifyContent:"center",
alignItems:"center"
},
input:{
height:30,
flex:1,
borderColor:"black",
borderWidth:1,
marginRight:10
},
showText:{
flex:1,
justifyContent:"center"
},
input_container:{
flexDirection:"row"
}
});
4. 离线缓存框架
离线缓存框架的引入是为了优化用户体验,在离线并且访问过某个数据后,在以后的某段时间我们将会使用缓存在本地的数据以提高APP响应速度。本质上该框架就是网络请求和本地缓存功能的结合:
流程图
页面代码:
import React,{Component} from "react";
import {View ,Text,StyleSheet,TextInput,Button} from "react-native";
import DataStore from "../expand/dao/DataStore";
const datastore = new DataStore();
export default class DataStoreDemoPage extends Component{
constructor(props){
super(props);
this.state = {
showText:"",
timestamp:""
};
}
loadData=()=>{
const url = `https://api.github.com/search/repositories?q=${this.searchKey}`;
datastore.fetchData(url).then((res)=>{
this.setState({
showText:JSON.stringify(res.data),
timestamp:new Date(res.timestamp).toString()
});
}).catch((error)=>{
console.log(error);
});
}
onChangeTextHandler=(text)=>{
this.searchKey = text;
}
render(){
return (
<View style={styles.container}>
<View style={styles.input_container}>
<TextInput style={styles.input} onChangeText={this.onChangeTextHandler}/>
<Button title="获取" onPress={this.loadData}></Button>
</View>
<Text style={styles.showText}>
{this.state.timestamp}
{this.state.showText}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:"#fff",
justifyContent:"center",
alignItems:"center"
},
input:{
height:30,
flex:1,
borderColor:"black",
borderWidth:1,
marginRight:10
},
showText:{
flex:1,
justifyContent:"center"
},
input_container:{
flexDirection:"row"
}
});
缓存框架核心代码 (DataStore.js)
import { AsyncStorage } from "react-native";
export default class DataStore {
/**
* 修饰内容数据
* @param {string} value
*/
_decorateData(value){
return { timestamp: new Date().getTime(),data: value }
}
/**
* 本地存储
* @param {string} key
* @param {string} value
*/
savaData = (key, value) => {
if (!key || !value) {
return;
}
let data = JSON.stringify(this._decorateData(value));
AsyncStorage.setItem(key, data).
catch((error) => {
console.log("local cache error:", error);
});
}
/**
* 获取本地数据
* @param {string} url
*/
fetchLocalData(url) {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(url).
then((res) => {
resolve(res);
}).
catch((error) => {
console.log(error);
reject(error);
});
});
}
/**
* 获取网络数据
* @param {string} url
*/
fetchNetData(url) {
return new Promise((resolve, reject) => {
fetch(url).
then((response) => {
if (response.ok) {
return response.json();
}
else {
throw new Error("Network response was not ok.");
}
}).
then((responseText) => {
//本地缓存
this.savaData(url, responseText);
resolve(responseText);
}).
catch((error) => {
reject(error);
});
});
}
/**
* 检查数据是否过期
* @param {string} url
*/
checkTimestamp(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;
}
/**
* 获取数据,优先查询本地数据,没有查询到或者过期则远程拉取数据
* @param {string} url
*/
fetchData(url) {
return new Promise((resolve, reject) => {
this.fetchLocalData(url).
then((res) => {
const cache = JSON.parse(res);
if (!res || !this.checkTimestamp(cache.timestamp)) {
this.fetchNetData(url).then((resJson) => {
resolve(this._decorateData(resJson));
}).catch((error) => {
console.log(error);
reject(error);
});
}
else {
resolve(cache || {});
}
}).catch((error) => {
console.log(error);
reject(error);
});
});
};
}
结语:
使用缓存框架可以极大的提升用户体验,在上面例子中load的时间是2s,而使用缓存后基本0.1s数据就出来了,合理使用缓存对你的app是有很大帮助的。不过由于持久化的底层操作是在本地写一个json文件,为了避免json过大需要合理优化缓存机制。