You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React游戏应用API数据缓存与离线运行方案咨询

基于RTK Query实现离线优先的游戏数据缓存方案

针对你的需求——持久化1000个API对象到localStorage、24小时自动更新、离线 fallback,用RTK Query(你已熟悉的工具)是最适配的现代化方案,以下是具体实现步骤:

1. 配置RTK Query缓存持久化到localStorage

RTK Query默认是内存缓存,结合redux-persist可以将缓存永久存储到localStorage:

依赖安装(若未安装)

npm install @reduxjs/toolkit react-redux redux-persist

核心代码实现

// store.js
import { configureStore } from '@reduxjs/toolkit';
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { persistReducer, persistStore } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // 默认使用localStorage

// 1. 定义游戏API切片
const gameApi = createApi({
  reducerPath: 'gameApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://myapi/' }),
  tagTypes: ['GameObject'], // 标记数据类型,方便后续更新
  endpoints: (builder) => ({
    // 获取单个游戏对象
    getGameObject: builder.query({
      query: (name) => `${name}`,
      // 核心缓存规则
      staleTime: 86400000, // 24小时内数据视为"新鲜",不自动触发更新
      keepUnusedDataFor: 31536000, // 缓存保留1年(近似永久存储)
      // 为每个对象生成唯一标签
      providesTags: (_, __, name) => [{ type: 'GameObject', id: name }],
      // 网络失败时不抛出错误,直接沿用缓存
      onQueryStarted: async (name, { queryFulfilled }) => {
        try {
          await queryFulfilled;
        } catch (err) {
          console.log(`无法更新${name},使用本地缓存数据`);
        }
      },
    }),
  }),
});

// 2. 配置redux-persist持久化规则
const persistConfig = {
  key: 'gameApiCache',
  storage,
  whitelist: ['queries', 'mutations'], // 仅持久化缓存数据,排除其他API状态
};

// 3. 包装API切片的reducer为持久化版本
const persistedGameApiReducer = persistReducer(persistConfig, gameApi.reducer);

// 4. 创建Redux Store
export const store = configureStore({
  reducer: {
    [gameApi.reducerPath]: persistedGameApiReducer,
    // 其他自定义reducer可在此添加
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false, // 关闭序列化检查,避免redux-persist的警告
    }).concat(gameApi.middleware),
});

// 5. 创建持久化Store实例
export const persistor = persistStore(store);

// 导出RTK Query生成的hooks
export const { useGetGameObjectQuery } = gameApi;

应用入口配置

在App根组件中用PersistGate包裹,确保缓存加载完成后再渲染应用:

// App.jsx
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './store';

function App() {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        {/* 你的游戏应用组件 */}
      </PersistGate>
    </Provider>
  );
}

export default App;

2. 核心逻辑说明

  • 24小时自动更新:staleTime设置为24小时,超过时间后,RTK Query会在后台自动发起API请求更新数据,同时返回旧缓存;如果离线,直接沿用旧数据。
  • 永久缓存:keepUnusedDataFor设置为1年,确保缓存不会被自动清理。
  • 离线 fallback:RTK Query默认会优先返回缓存数据,仅在缓存不存在时才发起网络请求;网络失败时,不会抛出错误,直接保留缓存数据。

3. 手动触发更新(可选)

如果需要手动刷新某个对象,可使用生成的hook提供的refetch方法:

// 组件中使用
import { useGetGameObjectQuery } from './store';

function GameObject({ name }) {
  const { data, refetch, isLoading } = useGetGameObjectQuery(name);

  if (isLoading && !data) return <div>加载中...</div>;

  return (
    <div>
      <h3>{data.name}</h3>
      <button onClick={() => refetch()}>刷新数据</button>
    </div>
  );
}

4. React Query替代方案(若考虑切换)

如果你想尝试React Query,配置更简洁,用@tanstack/react-query-persist-client插件直接实现localStorage持久化:

// App.jsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { persistQueryClient } from '@tanstack/react-query-persist-client';
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 86400000,
      cacheTime: 31536000,
      retry: false, // 离线时不重试
      refetchOnWindowFocus: false, // 窗口聚焦不自动刷新
    },
  },
});

const persister = createSyncStoragePersister({ storage: window.localStorage });
persistQueryClient({ queryClient, persister });

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      {/* 应用组件 */}
    </QueryClientProvider>
  );
}

组件中使用

import { useQuery } from '@tanstack/react-query';

function GameObject({ name }) {
  const { data, isLoading } = useQuery({
    queryKey: ['gameObject', name],
    queryFn: () => fetch(`https://myapi/${name}`).then(res => res.json()),
  });

  if (isLoading && !data) return <div>加载中...</div>;

  return <div>{data.name}</div>;
}

注意事项

  • 确保API返回的对象可以被localStorage序列化(无函数、循环引用等)。
  • localStorage有5MB左右的容量限制,1000个对象若体积过大,可考虑切换到IndexedDB,但需额外适配。
  • RTK Query的缓存键基于name参数自动生成,每个对象的缓存相互独立。

内容的提问来源于stack exchange,提问作者vogomatix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 04:52:35