Cloud Firestore 支持離線數據持久化。此功能會緩存您的應用正在使用的 Cloud Firestore 數據的副本,以便您的應用可以在設備離線時訪問這些數據。您可以寫入、讀取、收聽和查詢緩存數據。當設備重新聯機時,Cloud Firestore 會將您的應用所做的任何本地更改同步到 Cloud Firestore 後端。
要使用離線持久性,您無需對用於訪問 Cloud Firestore 數據的代碼進行任何更改。啟用離線持久性後,Cloud Firestore 客戶端庫會自動管理在線和離線數據訪問,並在設備重新在線時同步本地數據。
配置離線持久化
初始化 Cloud Firestore 時,您可以啟用或禁用離線持久性:
- 對於 Android 和 Apple 平台,默認啟用離線持久化。要禁用持久性,請將
PersistenceEnabled
選項設置為false
。 - 對於 Web,默認情況下禁用離線持久性。要啟用持久性,請調用
enablePersistence
方法。 Cloud Firestore 的緩存不會在會話之間自動清除。因此,如果您的 Web 應用程序處理敏感信息,請確保在啟用持久性之前詢問用戶他們是否在受信任的設備上。
Web modular API
// Memory cache is the default if no config is specified.
initializeFirestore(app);
// This is the default behavior if no persistence is specified.
initializeFirestore(app, {localCache: memoryLocalCache()});
// Defaults to single-tab persistence if no tab manager is specified.
initializeFirestore(app, {localCache: persistentLocalCache(/*settings*/{})});
// Same as `initializeFirestore(app, {localCache: persistentLocalCache(/*settings*/{})})`,
// but more explicit about tab management.
initializeFirestore(app,
{localCache:
persistentLocalCache(/*settings*/{tabManager: persistentSingleTabManager()})
});
// Use multi-tab IndexedDb persistence.
initializeFirestore(app,
{localCache:
persistentLocalCache(/*settings*/{tabManager: persistentMultipleTabManager()})
});
Web namespaced API
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() // Use memory-only cache settings.cacheSettings = MemoryCacheSettings(garbageCollectorSettings: MemoryLRUGCSettings()) // Use persistent disk cache, with 1 MB cache size settings.cacheSettings = PersistentCacheSettings(sizeBytes: 1_000_000) // Any additional options // ... // Enable offline data persistence let db = Firestore.firestore() db.settings = settings
目標-C
FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init]; // Use memory-only cache settings.cacheSettings = [[FIRMemoryCacheSettings alloc] initWithGarbageCollectorSettings:[[FIRMemoryLRUGCSettings alloc] init]]; // Use persistent disk cache (default behavior) // This example uses 1 million bytes, or 1 MB. settings.cacheSettings = [[FIRPersistentCacheSettings alloc] initWithSizeBytes:@1000000]; // Any additional options // ... // Enable offline data persistence FIRFirestore *db = [FIRFirestore firestore]; db.settings = settings;
Kotlin+KTX
val settings = firestoreSettings { // Use memory cache setLocalCacheSettings(memoryCacheSettings {}) // Use persistent disk cache (default) setLocalCacheSettings(persistentCacheSettings {}) } db.firestoreSettings = settings
Java
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder(db.getFirestoreSettings()) // Use memory-only cache .setLocalCacheSettings(MemoryCacheSettings.newBuilder().build()) // Use persistent disk cache (default) .setLocalCacheSettings(PersistentCacheSettings.newBuilder() .build()) .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 modular API
import { initializeFirestore, CACHE_SIZE_UNLIMITED } from "firebase/firestore"; const firestoreDb = initializeFirestore(app, { cacheSizeBytes: CACHE_SIZE_UNLIMITED });
Web namespaced API
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 // Set cache size to 1 MB settings.cacheSettings = PersistentCacheSettings(sizeBytes: 1_000_000) 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; // Set cache size to 1 MB settings.cacheSettings = [[FIRPersistentCacheSettings alloc] initWithSizeBytes:@1000000]; [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 modular API
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 namespaced API
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 modular API
import { disableNetwork } from "firebase/firestore"; await disableNetwork(db); console.log("Network disabled!"); // Do offline actions // ...
Web namespaced API
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 modular API
import { enableNetwork } from "firebase/firestore"; await enableNetwork(db); // Do online actions // ...
Web namespaced API
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 });