Appearance
本地存储(AsyncStorage)
1. AsyncStorage 简介
AsyncStorage 是 React Native 官方提供的持久化存储解决方案,用于在设备上存储键值对数据。它类似于 Web 开发中的 localStorage,但专门为 React Native 优化。
特点:
- 简单易用的键值对存储
- 异步操作(使用 Promise)
- 持久化存储,应用重启后数据仍然存在
- 适合存储小量数据(如用户偏好设置、认证令牌等)
限制:
- 存储容量有限(通常约 6MB)
- 不适合存储大量数据或复杂结构
- 存储的数据会被序列化,可能影响性能
2. 基础使用
2.1 安装
在 React Native 0.60+ 中,AsyncStorage 已从核心包中分离,需要单独安装:
bash
npm install @react-native-async-storage/async-storage
# 或
npx expo install @react-native-async-storage/async-storage2.2 导入
javascript
import AsyncStorage from '@react-native-async-storage/async-storage';2.3 基本操作
存储数据
javascript
const storeData = async (key, value) => {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(key, jsonValue);
console.log('数据存储成功');
} catch (e) {
console.error('存储数据失败:', e);
}
};
// 调用示例
storeData('userToken', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
storeData('userPreferences', { theme: 'dark', notifications: true });读取数据
javascript
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;
}
};
// 调用示例
const userToken = await getData('userToken');
const userPreferences = await getData('userPreferences');删除数据
javascript
const removeData = async (key) => {
try {
await AsyncStorage.removeItem(key);
console.log('数据删除成功');
} catch (e) {
console.error('删除数据失败:', e);
}
};
// 调用示例
removeData('userToken');清除所有数据
javascript
const clearAll = async () => {
try {
await AsyncStorage.clear();
console.log('所有数据已清除');
} catch (e) {
console.error('清除数据失败:', e);
}
};获取所有键
javascript
const getAllKeys = async () => {
try {
const keys = await AsyncStorage.getAllKeys();
console.log('所有存储的键:', keys);
return keys;
} catch (e) {
console.error('获取键失败:', e);
return [];
}
};3. 封装 AsyncStorage
为了更方便地使用 AsyncStorage,我们可以创建一个封装工具类:
javascript
// utils/storage.js
import AsyncStorage from '@react-native-async-storage/async-storage';
const storage = {
// 存储数据
set: async (key, value) => {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(key, jsonValue);
return true;
} catch (error) {
console.error('存储数据失败:', error);
return false;
}
},
// 读取数据
get: async (key) => {
try {
const jsonValue = await AsyncStorage.getItem(key);
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch (error) {
console.error('读取数据失败:', error);
return null;
}
},
// 删除数据
remove: async (key) => {
try {
await AsyncStorage.removeItem(key);
return true;
} catch (error) {
console.error('删除数据失败:', error);
return false;
}
},
// 清除所有数据
clear: async () => {
try {
await AsyncStorage.clear();
return true;
} catch (error) {
console.error('清除数据失败:', error);
return false;
}
},
// 获取所有键
getAllKeys: async () => {
try {
return await AsyncStorage.getAllKeys();
} catch (error) {
console.error('获取键失败:', error);
return [];
}
},
// 批量操作
multiGet: async (keys) => {
try {
const result = await AsyncStorage.multiGet(keys);
return result.map(([key, value]) => [key, JSON.parse(value)]);
} catch (error) {
console.error('批量获取失败:', error);
return [];
}
},
multiSet: async (keyValuePairs) => {
try {
const pairs = keyValuePairs.map(([key, value]) => [key, JSON.stringify(value)]);
await AsyncStorage.multiSet(pairs);
return true;
} catch (error) {
console.error('批量存储失败:', error);
return false;
}
},
multiRemove: async (keys) => {
try {
await AsyncStorage.multiRemove(keys);
return true;
} catch (error) {
console.error('批量删除失败:', error);
return false;
}
}
};
export default storage;4. 实际应用场景
4.1 用户认证令牌存储
javascript
import storage from './utils/storage';
// 登录成功后存储令牌
const login = async (username, password) => {
try {
// 模拟 API 调用
const response = await api.login(username, password);
const { token } = response.data;
// 存储令牌
await storage.set('authToken', token);
return true;
} catch (error) {
console.error('登录失败:', error);
return false;
}
};
// 检查是否已登录
const isLoggedIn = async () => {
const token = await storage.get('authToken');
return !!token;
};
// 登出
const logout = async () => {
await storage.remove('authToken');
};4.2 用户偏好设置
javascript
import storage from './utils/storage';
// 保存用户偏好
const saveUserPreferences = async (preferences) => {
await storage.set('userPreferences', preferences);
};
// 获取用户偏好
const getUserPreferences = async () => {
const defaultPreferences = {
theme: 'light',
notifications: true,
language: 'zh-CN'
};
const preferences = await storage.get('userPreferences');
return preferences || defaultPreferences;
};4.3 缓存数据
javascript
import storage from './utils/storage';
// 缓存 API 数据
const cacheApiData = async (endpoint, data) => {
const cacheKey = `api_cache_${endpoint}`;
const cacheData = {
data,
timestamp: Date.now(),
expiry: Date.now() + 3600000 // 1小时过期
};
await storage.set(cacheKey, cacheData);
};
// 获取缓存数据
const getCachedApiData = async (endpoint) => {
const cacheKey = `api_cache_${endpoint}`;
const cachedData = await storage.get(cacheKey);
if (!cachedData) return null;
// 检查是否过期
if (Date.now() > cachedData.expiry) {
await storage.remove(cacheKey);
return null;
}
return cachedData.data;
};5. 最佳实践
命名规范:使用前缀和命名空间来组织存储的键,避免冲突
javascript// 好的做法 const KEYS = { AUTH_TOKEN: '@MyApp:auth_token', USER_PREFS: '@MyApp:user_preferences', API_CACHE: '@MyApp:api_cache' };错误处理:始终处理 AsyncStorage 操作可能出现的错误
数据结构:对于复杂数据,先序列化再存储,读取时反序列化
存储限制:不要存储过大的数据,考虑使用其他存储方案(如 Realm、SQLite)
安全考虑:不要在 AsyncStorage 中存储敏感信息(如密码),考虑使用安全存储库
性能优化:
- 批量操作优于多次单独操作
- 避免在渲染过程中直接调用 AsyncStorage
- 考虑使用状态管理库来管理存储的数据
6. 常见问题与解决方案
6.1 数据存储失败
问题:AsyncStorage.setItem 失败
解决方案:
- 检查存储空间是否不足
- 确保数据可以被正确序列化
- 检查键名是否有效
6.2 数据读取为 null
问题:AsyncStorage.getItem 返回 null
解决方案:
- 检查键名是否正确
- 确保数据已正确存储
- 处理 null 值的情况
6.3 性能问题
问题:存储或读取大量数据时性能下降
解决方案:
- 分批处理大量数据
- 考虑使用更适合大量数据的存储方案
- 实现缓存策略,减少频繁读写
6.4 跨平台兼容性
问题:在不同平台上行为不一致
解决方案:
- 使用官方推荐的 @react-native-async-storage/async-storage
- 测试在 iOS 和 Android 上的表现
- 处理平台特定的边缘情况
7. 扩展阅读
8. 完整示例
javascript
// App.js
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
import storage from './utils/storage';
const App = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userData, setUserData] = useState(null);
// 检查登录状态
useEffect(() => {
checkLoginStatus();
}, []);
const checkLoginStatus = async () => {
const user = await storage.get('user');
if (user) {
setIsLoggedIn(true);
setUserData(user);
}
};
const handleLogin = async () => {
// 模拟登录
const user = {
username,
id: 1,
name: '测试用户'
};
await storage.set('user', user);
setIsLoggedIn(true);
setUserData(user);
};
const handleLogout = async () => {
await storage.remove('user');
setIsLoggedIn(false);
setUserData(null);
setUsername('');
setPassword('');
};
return (
<View style={styles.container}>
{isLoggedIn ? (
<View>
<Text style={styles.title}>欢迎回来,{userData?.name}!</Text>
<Text>用户名:{userData?.username}</Text>
<Button title="退出登录" onPress={handleLogout} />
</View>
) : (
<View>
<Text style={styles.title}>登录</Text>
<TextInput
style={styles.input}
placeholder="用户名"
value={username}
onChangeText={setUsername}
/>
<TextInput
style={styles.input}
placeholder="密码"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="登录" onPress={handleLogin} />
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
input: {
borderWidth: 1,
borderColor: '#ddd',
padding: 10,
marginBottom: 10,
borderRadius: 5,
},
});
export default App;这个示例展示了如何使用 AsyncStorage 实现简单的登录/退出功能,包括用户状态的持久化存储。
