Skip to content

本地存储(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-storage

2.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. 最佳实践

  1. 命名规范:使用前缀和命名空间来组织存储的键,避免冲突

    javascript
    // 好的做法
    const KEYS = {
      AUTH_TOKEN: '@MyApp:auth_token',
      USER_PREFS: '@MyApp:user_preferences',
      API_CACHE: '@MyApp:api_cache'
    };
  2. 错误处理:始终处理 AsyncStorage 操作可能出现的错误

  3. 数据结构:对于复杂数据,先序列化再存储,读取时反序列化

  4. 存储限制:不要存储过大的数据,考虑使用其他存储方案(如 Realm、SQLite)

  5. 安全考虑:不要在 AsyncStorage 中存储敏感信息(如密码),考虑使用安全存储库

  6. 性能优化

    • 批量操作优于多次单独操作
    • 避免在渲染过程中直接调用 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 实现简单的登录/退出功能,包括用户状态的持久化存储。

© 2026 编程马·菜鸟教程 版权所有