React Native 全栈开发实战班 - 网络与数据之数据缓存策略SWR、Query

embedded/2024/11/20 23:00:47/

在移动应用中,数据缓存 是提升应用性能和用户体验的重要手段。通过缓存数据,可以减少网络请求次数,降低延迟,提高应用在离线或网络不稳定情况下的可用性。React Native 提供了多种数据缓存策略,包括内存缓存、异步存储(AsyncStorage)以及第三方缓存库。本章节将详细介绍常见的数据缓存策略、实现方式以及最佳实践。


2.1 数据缓存概述

数据缓存 的目标是将从服务器获取的数据存储在本地,以便在后续请求中快速访问,而无需每次都进行网络请求。缓存策略的选择取决于数据的类型、使用频率、更新频率以及应用的具体需求。

常见的缓存策略包括:

  1. 内存缓存 将数据存储在内存中,访问速度快,但生命周期与组件生命周期相关。
  2. 异步存储(AsyncStorage): 将数据持久化存储在设备上,适用于需要长期保存的数据。
  3. 第三方缓存库:react-query, SWR 等,提供更强大的缓存管理和数据同步功能。

2.2 内存缓存

内存缓存 是将数据存储在内存中,访问速度快,适用于生命周期较短的数据。

2.2.1 使用 React 的 useStateuseEffect

可以通过 React 的 useStateuseEffect Hook 实现简单的内存缓存

示例:

// components/MemoryCacheExample.js
import React, { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';const MemoryCacheExample = () => {const [data, setData] = useState(null);const [loading, setLoading] = useState(false);const fetchData = async () => {setLoading(true);// 模拟网络请求await new Promise((resolve) => setTimeout(resolve, 1000));const fetchedData = { id: 1, title: 'Hello, World!' };setData(fetchedData);setLoading(false);};useEffect(() => {if (!data) {fetchData();}}, []);return (<View style={styles.container}>{loading ? (<Text>Loading...</Text>) : data ? (<Text style={styles.text}>{data.title}</Text>) : null}<Button title="Refresh" onPress={fetchData} /></View>);
};const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',},text: {fontSize: 18,marginBottom: 10,},
});export default MemoryCacheExample;

解释:

  • 数据存储在 data 状态中。
  • 组件挂载时,如果 data 为空,则发起网络请求。
  • 数据被存储在内存中,刷新页面时不会重新发起网络请求。
2.2.2 使用 React Context API

可以通过 Context API 实现全局内存缓存

示例:

// context/DataContext.js
import React, { createContext, useState, useEffect } from 'react';export const DataContext = createContext();export const DataProvider = ({ children }) => {const [data, setData] = useState(null);useEffect(() => {// 模拟数据获取const fetchData = async () => {await new Promise((resolve) => setTimeout(resolve, 1000));const fetchedData = { id: 1, title: 'Hello, Context!' };setData(fetchedData);};fetchData();}, []);return <DataContext.Provider value={data}>{children}</DataContext.Provider>;
};
// components/MemoryCacheWithContext.js
import React, { useContext } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { DataContext, DataProvider } from '../context/DataContext';const MemoryCacheWithContext = () => {const data = useContext(DataContext);const refreshData = () => {// 重新获取数据// 这里简单模拟,实际应用中需要更新 Context 的值alert('Refresh Data');};return (<View style={styles.container}>{data ? <Text style={styles.text}>{data.title}</Text> : <Text>Loading...</Text>}<Button title="Refresh" onPress={refreshData} /></View>);
};const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',},text: {fontSize: 18,marginBottom: 10,},
});export default MemoryCacheWithContext;

解释:

  • DataContext 提供全局数据存储。
  • 数据被存储在 Context 中,所有订阅的组件都可以访问到。

2.3 使用 AsyncStorage 进行持久化缓存

AsyncStorage 是 React Native 提供的一个简单的异步键值对存储系统,适用于持久化存储数据。

2.3.1 安装 AsyncStorage

对于 React Native 0.59 及以上版本,AsyncStorage 已经内置,无需额外安装。

对于 React Native 0.58 及以下版本,需要手动安装:

npm install @react-native-async-storage/async-storage
2.3.2 基本用法

存储数据:

import AsyncStorage from '@react-native-async-storage/async-storage';const storeData = async (key, value) => {try {const jsonValue = JSON.stringify(value);await AsyncStorage.setItem(key, jsonValue);} catch (e) {console.error(e);}
};

获取数据:

const getData = async (key) => {try {const jsonValue = await AsyncStorage.getItem(key);return jsonValue != null ? JSON.parse(jsonValue) : null;} catch (e) {console.error(e);return null;}
};

示例:

// components/AsyncStorageCacheExample.js
import React, { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';const AsyncStorageCacheExample = () => {const [data, setData] = useState(null);const [loading, setLoading] = useState(false);const fetchData = async () => {setLoading(true);// 模拟网络请求await new Promise((resolve) => setTimeout(resolve, 1000));const fetchedData = { id: 1, title: 'Hello, AsyncStorage!' };setData(fetchedData);await AsyncStorage.setItem('data_key', JSON.stringify(fetchedData));setLoading(false);};useEffect(() => {const loadData = async () => {const storedData = await AsyncStorage.getItem('data_key');if (storedData) {setData(JSON.parse(storedData));} else {fetchData();}};loadData();}, []);return (<View style={styles.container}>{loading ? (<Text>Loading...</Text>) : data ? (<Text style={styles.text}>{data.title}</Text>) : null}<Button title="Refresh" onPress={fetchData} /></View>);
};const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',},text: {fontSize: 18,marginBottom: 10,},
});export default AsyncStorageCacheExample;

解释:

  • 数据被存储在 data_key 键下。
  • 组件挂载时,先从 AsyncStorage 中获取数据,如果不存在,则发起网络请求并存储数据。
2.3.3 注意事项
  • 数据大小限制: AsyncStorage 适用于存储少量数据,不适合存储大量数据。
  • 性能: AsyncStorage 的性能不如内存缓存,不适合频繁读写操作。
  • 安全性: AsyncStorage 不提供加密功能,敏感数据需要谨慎处理。

2.4 使用第三方缓存

第三方缓存库提供了更强大的缓存管理和数据同步功能,适用于复杂的数据缓存需求。

2.4.1 react-query

react-query 是一个用于数据获取和缓存的库,支持自动重新获取数据、缓存失效、乐观更新等功能。

安装:

npm install @tanstack/react-query

示例:

// components/ReactQueryExample.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';const queryClient = new QueryClient();const fetchData = async () => {const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');return response.json();
};const MyQueryComponent = () => {const { data, error, isLoading } = useQuery(['post'], fetchData, {staleTime: 5 * 60 * 1000, // 数据缓存时间});if (isLoading) return <Text>Loading...</Text>;if (error) return <Text>Error: {error.message}</Text>;return <Text style={styles.text}>{data.title}</Text>;
};const ReactQueryExample = () => {return (<QueryClientProvider client={queryClient}><View style={styles.container}><MyQueryComponent /></View></QueryClientProvider>);
};const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',},text: {fontSize: 18,marginBottom: 10,},
});export default ReactQueryExample;

解释:

  • useQuery 钩子用于发起数据请求,并自动管理缓存
  • staleTime 定义数据缓存时间,超过时间后数据会被标记为过期,下次请求时会重新获取。
2.4.1 SWR 简介

SWR(Stale-While-Revalidate)是一个由 Vercel 开发的 React Hooks 库,用于数据获取和缓存。SWR 的核心思想是:在请求数据时,首先返回缓存中的旧数据(stale),然后在后台重新获取最新数据(revalidate),并将最新数据更新到缓存中。这种方式可以有效减少请求延迟,提高用户体验。

SWR 的主要特点:

  • 快速响应: 立即返回缓存数据,同时在后台重新获取最新数据。
  • 自动重新获取: 在组件重新挂载或窗口重新聚焦时,自动重新获取数据。
  • 错误重试: 在请求失败时,自动重试请求。
  • 乐观更新: 在数据提交时,先更新本地缓存,再提交到服务器。
  • 灵活的缓存策略: 支持自定义缓存逻辑和缓存失效时间。

安装 SWR:

npm install swr

2.4.2 基本用法

以下是一个使用 SWR 进行数据获取和缓存的示例。

示例:

// components/SWRExample.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import useSWR from 'swr';const fetcher = async (url) => {const response = await fetch(url);if (!response.ok) {throw new Error('Network response was not ok');}return response.json();
};const SWRExample = () => {const { data, error, isValidating } = useSWR('https://jsonplaceholder.typicode.com/posts/1', fetcher);if (error) return <Text style={styles.text}>Error: {error.message}</Text>;if (isValidating) return <Text style={styles.text}>Loading...</Text>;return (<View style={styles.container}><Text style={styles.text}>Title: {data.title}</Text><Text style={styles.text}>Body: {data.body}</Text></View>);
};const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',padding: 20,},text: {fontSize: 16,},
});export default SWRExample;

解释:

  • useSWR Hook:

    • 第一个参数是数据的唯一键(key),通常是请求的 URL。
    • 第二个参数是数据获取函数(fetcher),用于发起网络请求。
    • 返回一个对象,包含 data, error, isValidating 等属性。
  • 数据获取流程:

    • 组件挂载时,useSWR 会检查缓存中是否存在数据。
    • 如果有缓存数据,立即返回缓存数据,同时在后台重新获取最新数据。
    • 如果没有缓存数据,则发起网络请求。
  • 错误处理:

    • 如果请求失败,error 属性会包含错误信息。
  • 加载状态:

    • isValidating 属性表示数据是否正在重新获取。

2.4.3 缓存失效与重新获取

SWR 提供了多种方式来控制缓存失效和重新获取数据。

2.4.3.1 revalidateOnFocus

默认情况下,SWR 会在窗口重新聚焦时重新获取数据。可以通过 revalidateOnFocus 选项关闭此功能。

示例:

const { data, error, isValidating } = useSWR('https://jsonplaceholder.typicode.com/posts/1', fetcher, {revalidateOnFocus: false,
});
2.4.3.2 revalidateOnReconnect

默认情况下,SWR 会在网络重新连接时重新获取数据。可以通过 revalidateOnReconnect 选项关闭此功能。

示例:

const { data, error, isValidating } = useSWR('https://jsonplaceholder.typicode.com/posts/1', fetcher, {revalidateOnReconnect: false,
});
2.4.3.3 refreshInterval

可以通过 refreshInterval 选项设置定时重新获取数据的间隔时间(毫秒)。

示例:

const { data, error, isValidating } = useSWR('https://jsonplaceholder.typicode.com/posts/1', fetcher, {refreshInterval: 5000, // 每5秒重新获取一次数据
});
2.4.3.4 dedupingInterval

可以通过 dedupingInterval 选项设置请求去重的时间间隔(毫秒),避免在短时间内重复请求相同的数据。

示例:

const { data, error, isValidating } = useSWR('https://jsonplaceholder.typicode.com/posts/1', fetcher, {dedupingInterval: 1000, // 1秒内重复请求相同的数据会被去重
});

2.4.4 错误重试

SWR 支持自动重试失败的请求,可以通过 errorRetryCounterrorRetryInterval 选项控制重试次数和重试间隔。

示例:

const { data, error, isValidating } = useSWR('https://jsonplaceholder.typicode.com/posts/1', fetcher, {errorRetryCount: 3, // 最大重试次数errorRetryInterval: 5000, // 重试间隔时间
});

2.4.5 乐观更新

SWR 支持乐观更新,即在数据提交时,先更新本地缓存,再提交到服务器。

示例:

import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import useSWR, { mutate } from 'swr';const fetcher = async (url) => {const response = await fetch(url);if (!response.ok) {throw new Error('Network response was not ok');}return response.json();
};const OptimisticUpdateExample = () => {const { data, error, isValidating } = useSWR('https://jsonplaceholder.typicode.com/posts/1', fetcher);const updateTitle = () => {mutate('https://jsonplaceholder.typicode.com/posts/1', {...data,title: 'Updated Title',}, false); // 先更新缓存fetch('https://jsonplaceholder.typicode.com/posts/1', {method: 'PUT',headers: {'Content-Type': 'application/json',},body: JSON.stringify({...data,title: 'Updated Title',}),}).then((response) => response.json()).then((json) => {mutate('https://jsonplaceholder.typicode.com/posts/1'); // 重新获取最新数据}).catch((error) => {console.error(error);mutate('https://jsonplaceholder.typicode.com/posts/1', data, false); // 回滚缓存});};if (error) return <Text style={styles.text}>Error: {error.message}</Text>;if (isValidating) return <Text style={styles.text}>Loading...</Text>;return (<View style={styles.container}><Text style={styles.text}>Title: {data.title}</Text><Button title="Update Title" onPress={updateTitle} /></View>);
};const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',padding: 20,},text: {fontSize: 16,},
});export default OptimisticUpdateExample;

解释:

  • mutate 函数用于更新缓存数据。
  • 在数据提交前,先调用 mutate 更新缓存
  • 然后发起网络请求,如果请求成功,则重新获取最新数据。
  • 如果请求失败,则回滚缓存数据。

2.4.6 总结

SWR 是一个功能强大的数据获取和缓存库,适用于需要高效数据管理和缓存的应用。通过使用 SWR,可以简化数据获取逻辑,提高应用性能和用户体验。

作者简介

前腾讯电子签的前端负责人,现 whentimes tech CTO,专注于前端技术的大咖一枚!一路走来,从小屏到大屏,从 Web 到移动,什么前端难题都见过。热衷于用技术打磨产品,带领团队把复杂的事情做到极简,体验做到极致。喜欢探索新技术,也爱分享一些实战经验,帮助大家少走弯路!

温馨提示:可搜老码小张公号联系导师


http://www.ppmy.cn/embedded/139207.html

相关文章

#渗透测试#SRC漏洞挖掘#蓝队基础之网络七层杀伤链04 终章

网络杀伤链模型&#xff08;Kill Chain Model&#xff09;是一种用于描述和分析网络攻击各个阶段的框架。这个模型最初由洛克希德马丁公司提出&#xff0c;用于帮助企业和组织识别和防御网络攻击。网络杀伤链模型将网络攻击过程分解为多个阶段&#xff0c;每个阶段都有特定的活…

Spring Boot 中 Druid 连接池与多数据源切换的方法

Spring Boot 中 Druid 连接池与多数据源切换的方法 在Spring Boot项目中&#xff0c;使用Druid连接池和进行多数据源切换是常见的需求&#xff0c;尤其是在需要读写分离、数据库分片等复杂场景下。本文将详细介绍如何在Spring Boot中配置Druid连接池并实现多数据源切换。 一、…

ssm144基于SSM的校园二手物品交易平台+vue(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;校园二手物品交易平台设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本校园二手物品…

[STM32]从零开始的vs code 连接keil教程

一、前言 相信现在正在学习嵌入式或者是正在学习STM32的大家对keil都不陌生&#xff0c;keil是一款集成了多款芯片的嵌入式联合编译器。因为keil强大的芯片支持和及其易用的调试功能使其广受人们喜爱。但同时keil在某些方面也存在一定的问题。目前来说keil虽然有非常多好用的功…

安卓APK安装包arm64-v8a、armeabi-v7a、x86、x86_64有何区别?如何选择?

在GitHub网站下载Android 安装包&#xff0c;Actions资源下的APK文件通常有以下版本供选择&#xff1a; 例如上图是某Android客户端的安装包文件&#xff0c;有以下几个版本可以选择&#xff1a; mobile-release.apk&#xff08;通用版本&#xff0c;体积最大&#xff09;mobi…

llm 从0开始学习大语言模型, transformer架构学习

1. github&#xff1a; https://github.com/rasbt/LLMs-from-scratch 2. 这个是一本书&#xff0c;写在了github. 里面有代码&#xff0c;有讲解。从0开始写个llm 3. 如果看不懂&#xff0c;知乎有人写的中文版学习笔记&#xff1a; https://zhuanlan.zhihu.com/p/681401085

Pr:音频过渡

Adobe Premiere Pro 自带一组共三个音频过渡 Audio Transitions效果。 对音频剪辑之间应用交叉淡化 Crossfade过渡&#xff0c;操作方式类似于应用视频过渡效果。 对于交叉淡化&#xff0c;要保证前剪辑的出点之后及后剪辑的入点之前有足够的预留内容&#xff08;也称“手柄”&…

C语言之结构体

结构体 作用: 结构体是一种构造类型的数据结构 是一种或多种基本类型或构造类型的数据的集合 可自定义 定义: 1,一般定义方式 struct 结构体名称 { //定义变量 }; 2,定义结构体时顺带创建一个结构体变量 struct 结构体名称 { //定义变量 }结构体变量名; 3,给结构体起别名 typed…