Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

離線訪問數據

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

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
迅速
注意:此產品不適用於 watchOS 和 App Clip 目標。
let settings = FirestoreSettings()
settings.isPersistenceEnabled = true

// Any additional options
// ...

// Enable offline data persistence
let db = Firestore.firestore()
db.settings = settings
Objective-C
注意:此產品不適用於 watchOS 和 App Clip 目標。
FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init];
settings.persistenceEnabled = YES;

// Any additional options
// ...

// Enable offline data persistence
FIRFirestore *db = [FIRFirestore firestore];
db.settings = settings;

Java

FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
        .setPersistenceEnabled(true)
        .build();
db.setFirestoreSettings(settings);

Kotlin+KTX

val settings = firestoreSettings {
    isPersistenceEnabled = true
}
db.firestoreSettings = 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
});
迅速
注意:此產品不適用於 watchOS 和 App Clip 目標。
// 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
Objective-C
注意:此產品不適用於 watchOS 和 App Clip 目標。
// 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;
  

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);

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

Dart

db.settings = const Settings(
  persistenceEnabled: true,
  cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED,
);

收聽離線數據

當設備離線時,如果你啟用了離線持久化,當本地緩存的數據發生變化時,你的監聽器會收到監聽事件。您可以收聽文檔、集合和查詢。

要檢查您是從服務器接收數據還是從緩存接收數據,請在快照事件中使用SnapshotMetadata上的fromCache屬性。如果fromCachetrue ,則數據來自緩存並且可能是陳舊的或不完整的。如果fromCachefalse ,則數據是完整的並且是服務器上最新更新的最新數據。

默認情況下,如果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);
      });
  });
迅速
注意:此產品不適用於 watchOS 和 App Clip 目標。
// 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)")
}
Objective-C
注意:此產品不適用於 watchOS 和 App Clip 目標。
// 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);
    }];

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);
                }

            }
        });

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")
            }
        }

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 平台AndroidJavaScript SDK 提供了一個setIndexConfiguration方法,可讓您配置本地查詢索引以提高離線查詢的性能。

這些方法讀取用於在服務器上配置索引的相同 JSON 結構配置,遵循相同的索引定義格式

要使用的離線索引配置取決於您的應用在離線時會大量訪問哪些集合和文檔以及您想要的離線性能。雖然您可以導出後端索引配置以供在客戶端使用,但您的應用的離線訪問模式可能與在線訪問模式有很大不同,因此您的在線索引配置可能不適合離線使用。您希望您的應用以高性能離線訪問哪些集合和文檔?分析應用程序的行為後,請遵循索引指南中的索引定義原則。

要使離線索引配置可用於在您的客戶端應用程序中加載:

禁用和啟用網絡訪問

您可以使用以下方法禁用 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
        // ...
    });
迅速
注意:此產品不適用於 watchOS 和 App Clip 目標。
Firestore.firestore().disableNetwork { (error) in
    // Do offline things
    // ...
}
Objective-C
注意:此產品不適用於 watchOS 和 App Clip 目標。
[[FIRFirestore firestore] disableNetworkWithCompletion:^(NSError *_Nullable error) {
  // Do offline actions
  // ...
}];

Java

db.disableNetwork()
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                // Do offline things
                // ...
            }
        });

Kotlin+KTX

db.disableNetwork().addOnCompleteListener {
    // 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
        // ...
    });
迅速
注意:此產品不適用於 watchOS 和 App Clip 目標。
Firestore.firestore().enableNetwork { (error) in
    // Do online things
    // ...
}
Objective-C
注意:此產品不適用於 watchOS 和 App Clip 目標。
[[FIRFirestore firestore] enableNetworkWithCompletion:^(NSError *_Nullable error) {
  // Do online actions
  // ...
}];

Java

db.enableNetwork()
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                // Do online things
                // ...
            }
        });

Kotlin+KTX

db.enableNetwork().addOnCompleteListener {
    // Do online things
    // ...
}

Dart

db.enableNetwork().then((_) {
  // Back online
});