自学React-native (第九天)-- 网络编程以及离线缓存框架实现

本文详细介绍了React Native中的fetch函数,对比了其与xhr的区别,并探讨了AsyncStorage的使用,最后通过一个离线缓存框架实例展示了如何结合网络请求和本地缓存提升用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.前言:

react-native 提供了全局的fetch函数作为替代xhr的网络通讯模块。之所以这么选择是由于xhr存在以下的缺点:
1.设计上不符合责任分离原则,将输入、输出和用事件来跟踪的状态混杂在一个对象里。
2.与最近的promise以及基于生成器的异步编程模型不太搭。

需要注意的是,fetch规范与jquery.ajax主要有两种方式的不同:

  1. 当接收到一个代表错误的HTTP状态码的时候,从fetch返回的promise不会被标记为reject,即使该HTTP响应的状态码是404或者500,相反它会将Promise状态标记为resolve(但是会将resolve的返回值的ok属性设置为false),仅当网络故障时或请求被阻止时,才会标记为reject。
  2. 默认情况下,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过大需要合理优化缓存机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值