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

React Native(Expo)集成Firebase数据获取过慢:性能优化求助

Firebase Firestore数据获取耗时过长问题优化方案

问题背景

使用React Native(Expo)结合Firebase开发的投资类App,首页获取用户投资组合、交易记录、PNL等数据时,首次加载耗时约7秒,后续加载1-2秒,性能不符合预期。

当前实现代码

const Home = ({ navigation, route }) => {
  const userId = route.params?.userId;
  const [clientData, setClientData] = useState(null);
  const [latestAnnouncement, setLatestAnnouncement] = useState(null);

  useEffect(() => {
    if (!userId) {
      console.error("No user ID provided");
      return;
    }

    const fetchData = async () => {
      const startTimeClientData = Date.now();
      const clientRef = doc(FIRESTORE_DB, "clients", userId);
      const depositsRef = collection(clientRef, "deposits");
      const withdrawalsRef = collection(clientRef, "withdrawals");
      const dataPNLRef = query(collection(FIRESTORE_DB, "dataPNL"), orderBy("date", "desc"), limit(4));
    
      const [clientDoc, depositsSnapshot, withdrawalsSnapshot, dataPNLSnapshot] = await Promise.all([
        getDoc(clientRef),
        getDocs(depositsRef),
        getDocs(withdrawalsRef),
        getDocs(dataPNLRef),
      ]);
      console.log("Fetching client data:", Date.now() - startTimeClientData, "s");
    
      const dataPNL = dataPNLSnapshot.docs.map(doc => doc.data());
      const latestValue = dataPNL?.[0]?.value || 0;
      const initialDeposit = clientDoc.data().initialDeposit || 0;
      const totalDeposits = depositsSnapshot.docs.reduce((sum, doc) => sum + (doc.data().value || 0), 0);
      const totalWithdrawals = withdrawalsSnapshot.docs.reduce((sum, doc) => sum + (doc.data().value || 0), 0);
      let initialBalance = initialDeposit + totalDeposits - totalWithdrawals;
      if (totalDeposits === 0 && totalWithdrawals === 0) {
        initialBalance = initialDeposit;
      }
      const finalBalance = (Math.round(initialBalance * (1 + latestValue / 100) * 100) / 100).toFixed(2);
      const latestWithdrawal = withdrawalsSnapshot.docs[0]?.data()?.value || 0;
      const latestDeposit = depositsSnapshot.docs[0]?.data()?.value || 0;
      const monthlyPnL = dataPNL ? dataPNL.reduce((sum, data) => sum + (data?.value || 0), 0) : 0;

      setClientData({
        currentBalance: finalBalance,
        latestWithdrawal,
        latestDeposit,
        latestValue,
        monthlyPnL
      });
      const announcementsRef = collection(FIRESTORE_DB, "announcements");
      const latestAnnouncementRef = query(announcementsRef, orderBy("date", "desc"), limit(1));
      const latestAnnouncementSnapshot = await getDocs(latestAnnouncementRef);
    
      setLatestAnnouncement(latestAnnouncementSnapshot.docs[0]?.data());
    };

    fetchData();
  }, [userId]);

观察到的现象

  • 首次数据读取耗时约7秒,后续读取耗时1-2秒
  • 各请求耗时日志:
    LOG Fetching client data: 3030 ms
    LOG Fetching deposits: 827 ms
    LOG Fetching withdrawals: 2009 ms
    LOG Fetching dataPNL: 1168 ms
    LOG Fetching latest announcement: 158 ms
    

已尝试的无效方案

  • 拆分多个异步函数单独获取数据,耗时更长且不稳定
  • 为Firebase集合添加索引,无明显改善
  • 通过Cloud Functions在服务端处理数据后前端调用,效果不佳

针对性优化建议

1. 利用Firestore持久化缓存加速重复请求

首次加载慢源于冷缓存,后续快是命中本地缓存。显式开启持久化并优先读取缓存:

// 初始化Firestore时开启持久化缓存
import { initializeFirestore, persistentLocalCache } from "firebase/firestore";

const FIRESTORE_DB = initializeFirestore(app, {
  localCache: persistentLocalCache()
});

// 自定义缓存优先的请求方法
const getDocWithCache = async (ref, updateCallback) => {
  // 先读缓存更新UI
  const cachedDoc = await getDocFromCache(ref).catch(() => null);
  if (cachedDoc && updateCallback) updateCallback(cachedDoc.data());
  // 后台同步最新数据
  return await getDoc(ref);
};

对getDocs复用类似逻辑,先展示缓存数据,再静默更新最新内容,减少用户等待感。

2. 缩小数据读取范围,避免全量拉取

当前仅需deposits/withdrawals的总额和最新一笔记录,无需拉取所有文档:

// 只请求最新一笔交易记录
const depositsQuery = query(depositsRef, orderBy("date", "desc"), limit(1));
const withdrawalsQuery = query(withdrawalsRef, orderBy("date", "desc"), limit(1));

// 用Firestore聚合查询直接计算总额,替代前端reduce
const totalDepositsAgg = await getCountFromServer(query(depositsRef, select(aggregateField("value", "sum"))));
const totalWithdrawalsAgg = await getCountFromServer(query(withdrawalsRef, select(aggregateField("value", "sum"))));

// 并行发起所有请求
const [clientDoc, depositsSnapshot, withdrawalsSnapshot, dataPNLSnapshot, totalDeposits, totalWithdrawals] = await Promise.all([
  getDoc(clientRef),
  getDocs(depositsQuery),
  getDocs(withdrawalsQuery),
  getDocs(dataPNLRef),
  totalDepositsAgg,
  totalWithdrawalsAgg
]);

// 使用聚合结果
const totalDepositsValue = totalDeposits.data().sum || 0;
const totalWithdrawalsValue = totalWithdrawals.data().sum || 0;

大幅减少传输的数据量,降低请求耗时。

3. 并行处理所有异步请求

把latestAnnouncement请求加入Promise.all,避免串行等待:

const announcementsRef = collection(FIRESTORE_DB, "announcements");
const latestAnnouncementRef = query(announcementsRef, orderBy("date", "desc"), limit(1));

// 所有请求并行执行
const [clientDoc, depositsSnapshot, withdrawalsSnapshot, dataPNLSnapshot, latestAnnouncementSnapshot] = await Promise.all([
  getDoc(clientRef),
  getDocs(depositsRef),
  getDocs(withdrawalsRef),
  getDocs(dataPNLRef),
  getDocs(latestAnnouncementRef)
]);

// 处理完数据直接设置公告
setLatestAnnouncement(latestAnnouncementSnapshot.docs[0]?.data());

4. 检查Firebase数据库地理位置

确保Firestore数据库位置设置在用户主要所在区域(如国内用户选香港节点),跨区域请求会显著增加延迟,可在Firebase控制台Firestore设置中修改。

5. 简化前端计算逻辑

优化数据处理代码,减少不必要的运算:

// 简化初始余额计算
const initialBalance = initialDeposit + (totalDepositsValue || 0) - (totalWithdrawalsValue || 0);
// 直接数值运算替代重复取整
const finalBalance = (initialBalance * (1 + latestValue / 100)).toFixed(2);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 10:23:11