Cloud Firestore 支持离线数据持久化。此功能会缓存您的应用正在使用的 Cloud Firestore 数据的副本,以便您的应用可以在设备离线时访问这些数据。您可以写入、读取、收听和查询缓存数据。当设备重新联机时,Cloud Firestore 会将您的应用所做的任何本地更改同步到 Cloud Firestore 后端。
要使用离线持久性,您无需对用于访问 Cloud Firestore 数据的代码进行任何更改。启用离线持久性后,Cloud Firestore 客户端库会自动管理在线和离线数据访问,并在设备重新在线时同步本地数据。
配置离线持久化
初始化 Cloud Firestore 时,您可以启用或禁用离线持久性:
- 对于 Android 和 Apple 平台,默认启用离线持久化。要禁用持久性,请将
PersistenceEnabled
选项设置为false
。 - 对于 Web,默认情况下禁用离线持久性。要启用持久性,请调用
enablePersistence
方法。 Cloud Firestore 的缓存不会在会话之间自动清除。因此,如果您的 Web 应用程序处理敏感信息,请确保在启用持久性之前询问用户他们是否在受信任的设备上。
Web version 9
import { enableIndexedDbPersistence } from "firebase/firestore"; enableIndexedDbPersistence(db) .catch((err) => { if (err.code == 'failed-precondition') { // Multiple tabs open, persistence can only be enabled // in one tab at a a time. // ... } else if (err.code == 'unimplemented') { // The current browser does not support all of the // features required to enable persistence // ... } }); // Subsequent queries will use persistence, if it was enabled successfully
Web version 8
firebase.firestore().enablePersistence() .catch((err) => { if (err.code == 'failed-precondition') { // Multiple tabs open, persistence can only be enabled // in one tab at a a time. // ... } else if (err.code == 'unimplemented') { // The current browser does not support all of the // features required to enable persistence // ... } }); // Subsequent queries will use persistence, if it was enabled successfully
迅速
let settings = FirestoreSettings() settings.isPersistenceEnabled = true // Any additional options // ... // Enable offline data persistence let db = Firestore.firestore() db.settings = settings
目标-C
FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init]; settings.persistenceEnabled = YES; // Any additional options // ... // Enable offline data persistence FIRFirestore *db = [FIRFirestore firestore]; db.settings = settings;
Kotlin+KTX
val settings = firestoreSettings { isPersistenceEnabled = true } db.firestoreSettings = settings
Java
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder() .setPersistenceEnabled(true) .build(); db.setFirestoreSettings(settings);
Dart
// Apple and Android db.settings = const Settings(persistenceEnabled: true); // Web await db .enablePersistence(const PersistenceSettings(synchronizeTabs: true));
配置缓存大小
启用持久性后,Cloud Firestore 会缓存从后端接收的每个文档以供离线访问。 Cloud Firestore 设置缓存大小的默认阈值。超过默认值后,Cloud Firestore 会定期尝试清理旧的、未使用的文档。您可以配置不同的缓存大小阈值或完全禁用清理过程:
Web version 9
import { initializeFirestore, CACHE_SIZE_UNLIMITED } from "firebase/firestore"; const firestoreDb = initializeFirestore(app, { cacheSizeBytes: CACHE_SIZE_UNLIMITED });
Web version 8
firebase.firestore().settings({ cacheSizeBytes: firebase.firestore.CACHE_SIZE_UNLIMITED });
迅速
// The default cache size threshold is 100 MB. Configure "cacheSizeBytes" // for a different threshold (minimum 1 MB) or set to "FirestoreCacheSizeUnlimited" // to disable clean-up. let settings = Firestore.firestore().settings settings.cacheSizeBytes = FirestoreCacheSizeUnlimited Firestore.firestore().settings = settings
目标-C
// The default cache size threshold is 100 MB. Configure "cacheSizeBytes" // for a different threshold (minimum 1 MB) or set to "kFIRFirestoreCacheSizeUnlimited" // to disable clean-up. FIRFirestoreSettings *settings = [FIRFirestore firestore].settings; settings.cacheSizeBytes = kFIRFirestoreCacheSizeUnlimited; [FIRFirestore firestore].settings = settings;
Kotlin+KTX
// The default cache size threshold is 100 MB. Configure "setCacheSizeBytes" // for a different threshold (minimum 1 MB) or set to "CACHE_SIZE_UNLIMITED" // to disable clean-up. val settings = FirebaseFirestoreSettings.Builder() .setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED) .build() db.firestoreSettings = settings
Java
// The default cache size threshold is 100 MB. Configure "setCacheSizeBytes" // for a different threshold (minimum 1 MB) or set to "CACHE_SIZE_UNLIMITED" // to disable clean-up. FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder() .setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED) .build(); db.setFirestoreSettings(settings);
Dart
db.settings = const Settings( persistenceEnabled: true, cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED, );
听离线数据
当设备处于离线状态时,如果您启用了离线持久性,则当本地缓存的数据发生变化时,您的监听器将收到监听事件。您可以收听文档、收藏和查询。
要检查您是从服务器还是缓存接收数据,请在快照事件中使用SnapshotMetadata
的fromCache
属性。如果fromCache
为true
,则数据来自缓存并且可能陈旧或不完整。如果fromCache
为false
,则数据是完整的并且与服务器上的最新更新一致。
默认情况下,如果仅更改SnapshotMetadata
,则不会引发任何事件。如果您依赖于fromCache
值,请在附加侦听处理程序时指定includeMetadataChanges
侦听选项。
Web version 9
import { collection, onSnapshot, where, query } from "firebase/firestore"; const q = query(collection(db, "cities"), where("state", "==", "CA")); onSnapshot(q, { includeMetadataChanges: true }, (snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { console.log("New city: ", change.doc.data()); } const source = snapshot.metadata.fromCache ? "local cache" : "server"; console.log("Data came from " + source); }); });
Web version 8
db.collection("cities").where("state", "==", "CA") .onSnapshot({ includeMetadataChanges: true }, (snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { console.log("New city: ", change.doc.data()); } var source = snapshot.metadata.fromCache ? "local cache" : "server"; console.log("Data came from " + source); }); });
迅速
// Listen to metadata updates to receive a server snapshot even if // the data is the same as the cached data. db.collection("cities").whereField("state", isEqualTo: "CA") .addSnapshotListener(includeMetadataChanges: true) { querySnapshot, error in guard let snapshot = querySnapshot else { print("Error retreiving snapshot: \(error!)") return } for diff in snapshot.documentChanges { if diff.type == .added { print("New city: \(diff.document.data())") } } let source = snapshot.metadata.isFromCache ? "local cache" : "server" print("Metadata: Data fetched from \(source)") }
目标-C
// Listen to metadata updates to receive a server snapshot even if // the data is the same as the cached data. [[[db collectionWithPath:@"cities"] queryWhereField:@"state" isEqualTo:@"CA"] addSnapshotListenerWithIncludeMetadataChanges:YES listener:^(FIRQuerySnapshot *snapshot, NSError *error) { if (snapshot == nil) { NSLog(@"Error retreiving snapshot: %@", error); return; } for (FIRDocumentChange *diff in snapshot.documentChanges) { if (diff.type == FIRDocumentChangeTypeAdded) { NSLog(@"New city: %@", diff.document.data); } } NSString *source = snapshot.metadata.isFromCache ? @"local cache" : @"server"; NSLog(@"Metadata: Data fetched from %@", source); }];
Kotlin+KTX
db.collection("cities").whereEqualTo("state", "CA") .addSnapshotListener(MetadataChanges.INCLUDE) { querySnapshot, e -> if (e != null) { Log.w(TAG, "Listen error", e) return@addSnapshotListener } for (change in querySnapshot!!.documentChanges) { if (change.type == DocumentChange.Type.ADDED) { Log.d(TAG, "New city: ${change.document.data}") } val source = if (querySnapshot.metadata.isFromCache) "local cache" else "server" Log.d(TAG, "Data fetched from $source") } }
Java
db.collection("cities").whereEqualTo("state", "CA") .addSnapshotListener(MetadataChanges.INCLUDE, new EventListener<QuerySnapshot>() { @Override public void onEvent(@Nullable QuerySnapshot querySnapshot, @Nullable FirebaseFirestoreException e) { if (e != null) { Log.w(TAG, "Listen error", e); return; } for (DocumentChange change : querySnapshot.getDocumentChanges()) { if (change.getType() == Type.ADDED) { Log.d(TAG, "New city:" + change.getDocument().getData()); } String source = querySnapshot.getMetadata().isFromCache() ? "local cache" : "server"; Log.d(TAG, "Data fetched from " + source); } } });
Dart
db .collection("cities") .where("state", isEqualTo: "CA") .snapshots(includeMetadataChanges: true) .listen((querySnapshot) { for (var change in querySnapshot.docChanges) { if (change.type == DocumentChangeType.added) { final source = (querySnapshot.metadata.isFromCache) ? "local cache" : "server"; print("Data fetched from $source}"); } } });
获取离线数据
如果您在设备离线时获取文档,Cloud Firestore 会从缓存中返回数据。
查询集合时,如果没有缓存文档,则返回空结果。获取特定文档时,会返回错误。
查询离线数据
查询与离线持久性一起工作。您可以使用直接获取或通过侦听来检索查询结果,如前几节所述。您还可以在设备离线时对本地持久化数据创建新查询,但查询最初只会针对缓存的文档运行。
配置离线查询索引
默认情况下,Firestore SDK 在执行离线查询时会扫描其本地缓存中集合中的所有文档。使用此默认行为,当用户长时间处于离线状态时,离线查询性能可能会受到影响。
您可以通过配置本地查询索引来提高离线查询的性能:
迅速
Apple 平台 SDK 提供了一个setIndexConfiguration
方法,该方法读取用于在服务器上配置索引的相同 JSON 结构配置,遵循相同的索引定义格式。
// You will normally read this from a file asset or cloud storage. let indexConfigJson = """ { indexes: [ ... ], fieldOverrides: [ ... ] } """ // Apply the configuration. Firestore.firestore().setIndexConfiguration(indexConfigJson)
目标-C
Apple 平台 SDK 提供setIndexConfiguration
-读取用于在服务器上配置索引的相同 JSON 结构配置的方法,遵循相同的索引定义格式。
// You will normally read this from a file asset or cloud storage. NSString *indexConfigJson = @" { " " indexes: [ " " ... " " ], " " fieldOverrides: [ " " ... " " ] " " } "; // Apply the configuration. [[FIRFirestore firestore] setIndexConfigurationFromJSON:indexConfigJson completion:^(NSError * _Nullable error) { // ... }];
Java
Android SDK 提供了一个setIndexConfiguration
方法,它读取用于在服务器上配置索引的相同 JSON 结构配置,遵循相同的索引定义格式。
// You will normally read this from a file asset or cloud storage. String indexConfigJson = " { " + " indexes: [ " + " ... " + " ], " + " fieldOverrides: [ " + " ... " + " ] " + " } "; // Apply the configuration. FirebaseFirestore.getInstance().setIndexConfiguration(indexConfigJson);
Kotlin+KTX
Android SDK 提供了一个setIndexConfiguration
方法,它读取用于在服务器上配置索引的相同 JSON 结构配置,遵循相同的索引定义格式。
// You will normally read this from a file asset or cloud storage. val indexConfigJson = """ { indexes: [ ... ], fieldOverrides: [ ... ] } """ // Apply the configuration. FirebaseFirestore.getInstance().setIndexConfiguration(indexConfigJson)
Dart
Flutter SDK 提供了一个setIndexConfigurationFromJSON
方法,该方法读取用于在服务器上配置索引的相同 JSON 结构配置,遵循相同的索引定义格式。
// You will normally read this from a file asset or cloud storage. var indexConfigJson = """ { indexes: [ ... ], fieldOverrides: [ ... ] } """; // Apply the configuration. await FirebaseFirestore.instance.setIndexConfigurationFromJSON(json: indexConfigJson);
或者,您可以使用setIndexConfiguration
方法通过基于类的 API 配置索引。
var indexes = [ Index( collectionGroup: "posts", queryScope: QueryScope.collection, fields: [ IndexField(fieldPath: "author", arrayConfig: ArrayConfig.contains), IndexField(fieldPath: "timestamp", order: Order.descending) ], ), ]; await FirebaseFirestore.instance.setIndexConfiguration(indexes: indexes);
要使用的离线索引配置取决于您的应用程序在离线时大量访问的集合和文档以及您想要的离线性能。虽然您可以导出后端索引配置以在客户端上使用,但您应用的离线访问模式可能与在线访问模式有很大不同,因此您的在线索引配置可能不适合离线使用。您希望您的应用程序以高性能离线访问哪些集合和文档?分析完应用程序的行为后,请遵循索引指南中的索引定义原则。
要使离线索引配置可用于在您的客户端应用程序中加载:
- 使用您的应用程序编译和分发它们
- 从 CDN 下载它们
- 从Cloud Storage for Firebase等存储系统中获取它们。
禁用和启用网络访问
您可以使用以下方法禁用 Cloud Firestore 客户端的网络访问。当禁用网络访问时,所有快照侦听器和文档请求都会从缓存中检索结果。写入操作排队,直到重新启用网络访问。
Web version 9
import { disableNetwork } from "firebase/firestore"; await disableNetwork(db); console.log("Network disabled!"); // Do offline actions // ...
Web version 8
firebase.firestore().disableNetwork() .then(() => { // Do offline actions // ... });
迅速
Firestore.firestore().disableNetwork { (error) in // Do offline things // ... }
目标-C
[[FIRFirestore firestore] disableNetworkWithCompletion:^(NSError *_Nullable error) { // Do offline actions // ... }];
Kotlin+KTX
db.disableNetwork().addOnCompleteListener { // Do offline things // ... }
Java
db.disableNetwork() .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { // Do offline things // ... } });
Dart
db.disableNetwork().then((_) { // Do offline things });
使用以下方法重新启用网络访问:
Web version 9
import { enableNetwork } from "firebase/firestore"; await enableNetwork(db); // Do online actions // ...
Web version 8
firebase.firestore().enableNetwork() .then(() => { // Do online actions // ... });
迅速
Firestore.firestore().enableNetwork { (error) in // Do online things // ... }
目标-C
[[FIRFirestore firestore] enableNetworkWithCompletion:^(NSError *_Nullable error) { // Do online actions // ... }];
Kotlin+KTX
db.enableNetwork().addOnCompleteListener { // Do online things // ... }
Java
db.enableNetwork() .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { // Do online things // ... } });
Dart
db.enableNetwork().then((_) { // Back online });