我如何将 Firebase 加载时间从 5 秒降低到低于 100 毫秒
Source: Dev.to
问题
当 Muslifie 推出时,旅游列表页面的加载时间为 2–5 秒。在巴基斯坦、埃及和印度尼西亚等网络较慢的地区,用户在数据出现之前就已经离开了。
罪魁祸首是什么?每次用户打开应用时,Firebase 都会执行全新的 Firestore 查询——没有缓存,没有批处理,只是每次打开都进行原始读取。
在 200 多个导游档案并且还在增长的情况下,这既昂贵又慢。
我最初的直觉是使用 Flutter 内置的 Firestore 持久化进行客户端缓存。它稍有帮助,但:
- 首次加载仍然很慢
- 重新安装后数据会变陈旧
- 无法控制缓存失效
对于导游每日可用性会变化的市场来说,这还不够。
解决方案
将繁重的工作转移到 Firebase Cloud Functions 并使用内存缓存。
// functions/index.js
let cachedData = null;
let cacheTime = null;
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
exports.getTours = functions.https.onCall(async (data, context) => {
const now = Date.now();
// Return cache if still fresh
if (cachedData && (now - cacheTime) ({ id: doc.id, ...doc.data() }));
cacheTime = now;
return cachedData;
});关键洞察: Cloud Function 实例在调用之间保持温热,因此只要实例存活(通常为 15–30 分钟),缓存就会驻留在内存中。
冷启动后第一次调用函数的用户需要等待约 400 毫秒。随后每个用户都能在 100 毫秒以下 获得缓存数据。
指标
| 指标 | 之前 | 之后 |
|---|---|---|
| 首次加载 | 2–5 秒 | 100 毫秒以下 |
| 每日 Firestore 读取次数 | 40,000+ | ~800 |
| Firebase 费用 | 快速上升 | 持平 |
感知性能
性能提升只有在用户在第一次冷启动期间不盯着空白屏幕时才有效。我配合 Flutter 骨架加载器 使用:
// lib/widgets/tour_list.dart
Widget build(BuildContext context) {
return FutureBuilder>(
future: fetchTours(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return TourSkeletonList(); // Show skeleton while loading
}
return TourList(tours: snapshot.data!);
},
);
}感知性能与实际性能同等重要。如果用户能看到一些正在进行的内容,他们会容忍加载过程。
未来改进
如果今天重新构建 Muslifie,我会加入:
- 通过 Firebase Extensions 使用 Redis 缓存,实现多实例缓存共享
- 增量加载 —— 首先即时显示前 10 条旅游,其余在后台加载
- 应用启动时预取 —— 在用户甚至未导航之前就开始获取数据
收获
- 不要过早优化;先进行性能分析。
- 瓶颈并不是 Flutter 小部件或网络速度,而是多余的 Firestore 读取。
- 一个只有 20 行的缓存函数就能显著降低读取次数和成本。
- 为每次数据获取添加日志,观察时间到底花在了哪里;答案往往比想象的更简单。
最初发布于 buildzn.com.