在移动应用中,数据缓存 是提升应用性能和用户体验的重要手段。通过缓存数据,可以减少网络请求次数,降低延迟,提高应用在离线或网络不稳定情况下的可用性。React Native 提供了多种数据缓存策略,包括内存缓存、异步存储(AsyncStorage)以及第三方缓存库。本章节将详细介绍常见的数据缓存策略、实现方式以及最佳实践。
2.1 数据缓存概述
数据缓存 的目标是将从服务器获取的数据存储在本地,以便在后续请求中快速访问,而无需每次都进行网络请求。缓存策略的选择取决于数据的类型、使用频率、更新频率以及应用的具体需求。
常见的缓存策略包括:
- 内存缓存: 将数据存储在内存中,访问速度快,但生命周期与组件生命周期相关。
- 异步存储(AsyncStorage): 将数据持久化存储在设备上,适用于需要长期保存的数据。
- 第三方缓存库: 如
react-query
,SWR
等,提供更强大的缓存管理和数据同步功能。
2.2 内存缓存
内存缓存 是将数据存储在内存中,访问速度快,适用于生命周期较短的数据。
2.2.1 使用 React 的 useState
和 useEffect
可以通过 React 的 useState
和 useEffect
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;
解释:
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;
解释:
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
等属性。
-
数据获取流程:
-
错误处理:
- 如果请求失败,
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 支持自动重试失败的请求,可以通过 errorRetryCount
和 errorRetryInterval
选项控制重试次数和重试间隔。
示例:
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;
解释:
2.4.6 总结
SWR 是一个功能强大的数据获取和缓存库,适用于需要高效数据管理和缓存的应用。通过使用 SWR,可以简化数据获取逻辑,提高应用性能和用户体验。
作者简介
前腾讯电子签的前端负责人,现 whentimes tech CTO,专注于前端技术的大咖一枚!一路走来,从小屏到大屏,从 Web 到移动,什么前端难题都见过。热衷于用技术打磨产品,带领团队把复杂的事情做到极简,体验做到极致。喜欢探索新技术,也爱分享一些实战经验,帮助大家少走弯路!
温馨提示:可搜老码小张公号联系导师