Flutter/Dart环境下Firestore外键对象懒加载最佳实践问询
推荐最佳实践
首先优先推荐使用官方Firebase ODM for Flutter方案,它原生复刻了类似Ormlite的外键字段懒加载逻辑,无需自行封装基础能力:
- 定义模型时直接使用
DocumentReference<T>类型标注关联字段,初始仅存储引用路径,不会自动拉取关联数据 - 需要加载关联对象时调用对应reference的
get()方法即可,框架自动处理序列化/反序列化逻辑,无需手动处理id映射和对象缓存 - 内置缓存能力,重复调用同一个reference的get方法不会重复触发网络请求
如果需要自定义实现,你提到的第二种Reference<T>泛型封装方案是最优选择,相比另外两种方案优势明显:
- 完全隔离模型层和业务逻辑,不会违反MVC/Bloc的分层规则,数据拉取逻辑可以统一封装在数据层注入到Reference实例中
- 无需编写大量模板代码,结合
build_runner工具可以自动为所有关联字段生成Reference包裹逻辑 - 可以很方便地扩展增加缓存、预加载、状态监听等能力,不会侵入原有数据模型的定义
改良版Reference类实现参考
class Reference<T> { final String id; final Future<T> Function(String id) _fetcher; T? _cachedObject; bool _isFetching = false; Reference(this.id, this._fetcher); Future<T> get object async { if (_cachedObject != null) return _cachedObject!; if (_isFetching) { // 避免重复并发请求 await Future.doWhile(() => Future.delayed(const Duration(milliseconds: 100), () => _isFetching)); return _cachedObject!; } _isFetching = true; try { _cachedObject = await _fetcher(id); return _cachedObject!; } finally { _isFetching = false; } } // 同步获取缓存,不需要触发拉取时使用 T? get cachedObject => _cachedObject; // 清空缓存,需要刷新数据时调用 void clearCache() => _cachedObject = null; }
对应数据模型改写示例
class Parent { String id; Reference<Child> child; } class Child { String id; String description = 'child of parent'; Reference<ParentCousin> parentCousin; } class ParentCousin { String id; Reference<ChildCousin> child; } class ChildCousin { String id; String description = 'child of ParentCousin'; }
不推荐另外两种方案的原因:
- 用Map包裹的方案类型不安全,编译期无法检测类型错误,后期维护成本很高
- 占位子对象的方案确实会侵入模型层,把加载状态逻辑混入纯数据模型,不符合分层设计规范
内容的提问来源于stack exchange,提问作者lhengl
相关产品推荐
相关产品推荐

