【免费下载链接】react-native-mmkv
⚡️ The fastest key/value storage for React Native. ~30x faster than AsyncStorage!
react-query(TanStack Query)默认把请求缓存保存在内存中,应用重启后缓存即失效。本文讲解如何在 React Native 项目中,把 react-query 的持久化层从 AsyncStorage 无缝切换到 react-native-mmkv——一个基于 C++ 原生实现、完全同步调用的高性能键值存储库,让查询缓存以字符串形式落盘,实现"重启不丢缓存、离线秒开页面"。读完本文,你将掌握query-async-storage-persister与 MMKV 的桥接写法、PersistQueryClientProvider的接入方式,以及 MMKV 实例的配置细节与底层行为。
一、为什么把 react-query 缓存持久化到 MMKV
react-query 提供了createAsyncStoragePersister插件,用于把查询缓存(Query Cache)写入一个符合AsyncStorage接口的存储介质。默认情况下它面向@react-native-async-storage/async-storage,但该接口只需三个方法:setItem、getItem、removeItem。
MMKV 的优势在于:
- 完全同步:
storage.set(key, value)、storage.getString(key)等都是同步返回,没有 Promise、没有 Bridge,读写是直接的 JS ↔ C++ 调用,符合"读取一次缓存"的高频场景; - 类型丰富:支持
string | boolean | number | ArrayBuffer四种取值,天然适合以字符串存储的 JSON 序列化缓存; - 原生性能:底层是腾讯开源的 MMKV C++ 实现,读写性能远高于跨线程桥接的 AsyncStorage(仓库主页标注"~30x faster than AsyncStorage",该数据来自 StorageBenchmark 基准项目,仅为社区参考)。
因此,把 react-query 的持久化目标从 AsyncStorage 换成 MMKV,既保持了createAsyncStoragePersister的使用方式不变,又能获得同步、快速的落盘体验。
二、前置条件:安装依赖
本文方案需要两个层面的依赖:
- react-native-mmkv 本身(本仓库的安装方式):
npm install react-native-mmkv react-native-nitro-modules cd ios && pod install若使用 Expo,则:
npx expo install react-native-mmkv react-native-nitro-modules npx expo prebuild- react-query 的持久化插件(下文核心步骤)。
注意:react-native-mmkv V4 基于 Nitro Modules 架构,安装时需保证
react-native-nitro-modules已正确链接;从 V3 升级请参考 V4 升级指南。
三、核心步骤一:安装 react-query 持久化包
执行以下命令,安装 TanStack Query 的持久化辅助包:
yarn add @tanstack/query-async-storage-persister @tanstack/react-query-persist-client两个包的分工:
@tanstack/query-async-storage-persister:提供createAsyncStoragePersister(...),把符合 AsyncStorage 接口的存储对象包装成 react-query 可用的Persister;@tanstack/react-query-persist-client:提供PersistQueryClientProvider根组件,负责在应用启动时恢复缓存、运行时持续持久化。
四、核心步骤二:编写 MMKV 适配器(clientStorage)
createAsyncStoragePersister需要一个实现setItem / getItem / removeItem的存储对象。由于 MMKV 本身是同步 API,这个适配器写起来非常直接:
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister' import { createMMKV } from "react-native-mmkv" const storage = createMMKV() const clientStorage = { setItem: (key, value) => { storage.set(key, value); }, getItem: (key) => { const value = storage.getString(key); return value === undefined ? null : value; }, removeItem: (key) => { storage.remove(key); }, }; export const clientPersister = createAsyncStoragePersister({ storage: clientStorage });代码中的几个关键细节:
createMMKV()的默认行为:无参调用会创建一个 ID 为mmkv.default的默认实例(见 createMMKV.ts 与 MMKVFactory.nitro.ts 的@default 'mmkv.default')。建议将storage与clientPersister在模块顶层导出,全应用复用同一个实例,而不是每次渲染都新建。getItem必须把undefined转成null:MMKV 的getString在键不存在时返回undefined(接口签名见 MMKV.nitro.ts),而 AsyncStorage 接口约定"键不存在返回null"。value === undefined ? null : value正是为了满足这一契约,避免 persister 把undefined误当作合法值处理。- 返回值类型:react-query 持久化的缓存内容是 JSON 序列化后的字符串,
storage.set(key, value)写入的正是字符串类型;set方法同时支持boolean | number | ArrayBuffer,但此处无需使用。
从源码看,MMKV 的set / getString / remove均为同步方法,因此适配器无需async/await,也无需返回 Promise——这比基于 AsyncStorage 的异步适配器更简单直接。
五、核心步骤三:用 PersistQueryClientProvider 接入根组件
创建好clientPersister后,在根组件(如App.tsx)中用PersistQueryClientProvider替换普通的QueryClientProvider:
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client' const App = () => { return ( <PersistQueryClientProvider persistOptions={{ persister: clientPersister }}> {...} </PersistQueryClientProvider> ); };persistOptions.persister即上一步导出的clientPersister。这样,应用每次启动时:
PersistQueryClientProvider会先从 MMKV 中读取上一次保存的缓存(走clientStorage.getItem);- 恢复为可用的 Query Cache,未过期的查询无需重新请求即可立即渲染;
- 运行时查询状态的任何变化都会经
clientStorage.setItem / removeItem写回 MMKV。
提示:
persistOptions还支持maxAge(缓存最大存活时间)、buster(缓存失效标识)等可选参数,可按 TanStack Query 官方文档的createAsyncStoragePersister说明按需配置。
六、源码级深入:MMKV 实例的配置与行为
6.1 可配置项(Configuration)
createMMKV(configuration?)接受完整的配置对象,相关类型定义见 MMKVFactory.nitro.ts:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
id | string | 'mmkv.default' | 实例 ID,多实例场景必须使用不同 ID |
path | string | undefined | 存储根目录,默认$(Documents)/mmkv/;iOS 上配置了 AppGroup 且未指定 path 时自动使用 AppGroup 目录 |
encryptionKey | string | undefined | 加密密钥,AES-128 最长 16 字节,AES-256 最长 32 字节 |
encryptionType | 'AES-128' \| 'AES-256' | 'AES-128' | 加密算法 |
mode | 'single-process' \| 'multi-process' | 'single-process' | 多进程模式(App Clip、扩展、后台服务等) |
readOnly | boolean | false | 只读模式,只允许读,set会抛错 |
compareBeforeSet | boolean | false | 写入前先比较新旧值,相等则跳过文件写入,作为可选性能优化 |
recoveryStrategy | 'discard-on-error' \| 'recover-on-error' | undefined | 存储出现 CRC/长度错误时的恢复策略 |
针对 react-query 持久化场景,常见进阶写法:
// 多用户场景:按用户隔离查询缓存 export const queryStorage = createMMKV({ id: `query-cache-${userId}` }) // 敏感查询缓存:加密落盘 export const secureStorage = createMMKV({ id: 'secure-query-cache', encryptionKey: 'my-encryption-key!', encryptionType: 'AES-256', })6.2 底层方法签名
适配器用到的三个方法在 MMKV.nitro.ts 中定义如下:
set(key: string, value: boolean | string | number | ArrayBuffer): void(L44-L44)——空 key 会抛错;getString(key: string): string | undefined(L56-L56)——键不存在返回undefined,这正是适配器需要转null的原因;remove(key: string): boolean(L77-L77)——键被删除返回true,否则返回false。
此外 MMKV 实例还提供getBoolean / getNumber / getBuffer / contains / getAllKeys / clearAll / trim等方法,以及addOnValueChangedListener值变更监听能力,可在需要同步多实例缓存时使用。
6.3 平台与测试环境行为
- Web 平台:仓库提供独立的 Web 实现(createMMKV.web.ts,底层基于 localStorage 的读写),适配器写法保持一致;
- 测试环境:在 Jest 等测试环境中,
createMMKV会返回内存 Mock 实例(见 createMockMMKV.ts),其getString对缺失键同样返回undefined、set对空 key 抛错,行为与真实实现对齐,因此你的clientStorage适配器可以直接在单测中验证(例如断言"写入后可读回、缺失键返回 null")。
七、实践建议与注意事项
- 始终导出并复用实例:在模块顶层创建
storage与clientPersister并导出,避免每次渲染新建 MMKV 实例导致重复打开文件句柄。 getItem的null转换不可省略:直接返回storage.getString(key)的undefined会导致 persister 行为异常,务必按文档写法处理。- 持久化内容为字符串:react-query 缓存以 JSON 字符串形式存取,
storage.set(key, value)不会做类型推断,直接写入即可;如需额外存布尔/数字型业务数据,MMKV 也原生支持。 - 多实例隔离:需要按用户或按模块隔离查询缓存时,使用不同的
id创建多个实例,并在适配器中分别引用。 - 敏感数据可加密:若查询缓存包含 token、个人信息等,可配置
encryptionKey+encryptionType: 'AES-256',密钥长度须符合算法要求。 - 清除缓存:需要"登出清缓存"时,可调用
storage.clearAll()清空该实例全部键值,或storage.remove(key)定向删除某个缓存条目。
八、同系列方案对比(横向参考)
本仓库 docs 目录下还提供了其他状态管理库的 MMKV 适配模板,可对照参考:
- Zustand persist-middleware 适配:同样实现
setItem / getItem / removeItem三方法,getItem使用value ?? null的写法(MMKV 返回undefined时经空值合并得到null); - Redux-persist 适配:由于 redux-persist 接口是异步的,适配器将同步调用包装进
Promise.resolve(...)返回; - Jotai、MobX、Recoil、TinyBase 等适配:思路一致,均是把各库的存储接口映射到 MMKV 的同步 API 上。
可以看到,React Query 的适配器之所以无需 Promise 包装,正是因为createAsyncStoragePersister直接接受同步风格的 AsyncStorage 兼容对象,这也是三者中写法最简洁的一个。
九、小结
通过三个步骤即可完成 react-query 查询缓存向 MMKV 的迁移:
- 安装
@tanstack/query-async-storage-persister与@tanstack/react-query-persist-client; - 用
createMMKV()+set / getString / remove实现clientStorage,并牢记getItem的undefined → null转换,再经createAsyncStoragePersister生成clientPersister; - 在根组件用
PersistQueryClientProvider包裹应用并传入persistOptions.persister。
结合仓库源码(createMMKV.ts、MMKV.nitro.ts、MMKVFactory.nitro.ts)可以看到,这一方案充分利用了 MMKV 同步、快速、类型丰富的特性,让 react-query 的缓存持久化获得原生级的读写性能,且适配代码量极小、易于测试与维护。
【免费下载链接】react-native-mmkv
⚡️ The fastest key/value storage for React Native. ~30x faster than AsyncStorage!
相关推荐
react-native-mmkv 与 Recoil 集成:用 atomEffect 实现 atom 状态持久化
react native mmkv 与 Recoil 集成:用 atomEffect 实现 atom 状态持久化 Recoil 的 atom 状态默认只存在于内
react-native-mmkv 的 redux-persist 存储封装:用 MMKV 同步持久化 Redux 状态
react native mmkv 的 redux persist 存储封装:用 MMKV 同步持久化 Redux 状态 本文基于仓库文档 docs/WRAPP
用 @instantdb/react-native-mmkv 为 Instant 的 React Native 应用接入 MMKV 本地存储
用 @instantdb/react native mmkv 为 Instant 的 React Native 应用接入 MMKV 本地存储 本指南以 clie
后端数据库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考