Catch up on highlights from Firebase at Google I/O 2023. Learn more

Truy cập dữ liệu ngoại tuyến

Cloud Firestore hỗ trợ lưu trữ dữ liệu ngoại tuyến. Tính năng này lưu vào bộ đệm một bản sao dữ liệu Cloud Firestore mà ứng dụng của bạn đang sử dụng tích cực, vì vậy, ứng dụng của bạn có thể truy cập dữ liệu khi thiết bị ngoại tuyến. Bạn có thể viết, đọc, nghe và truy vấn dữ liệu được lưu trong bộ nhớ cache. Khi thiết bị trực tuyến trở lại, Cloud Firestore sẽ đồng bộ hóa mọi thay đổi cục bộ do ứng dụng của bạn thực hiện với phần phụ trợ của Cloud Firestore.

Để sử dụng tính bền vững ngoại tuyến, bạn không cần thực hiện bất kỳ thay đổi nào đối với mã mà bạn sử dụng để truy cập dữ liệu Cloud Firestore. Khi bật tính năng lưu trữ ngoại tuyến, thư viện máy khách Cloud Firestore sẽ tự động quản lý quyền truy cập dữ liệu trực tuyến và ngoại tuyến, đồng thời đồng bộ hóa dữ liệu cục bộ khi thiết bị trực tuyến trở lại.

Định cấu hình tính bền vững ngoại tuyến

Khi bạn khởi chạy Cloud Firestore, bạn có thể bật hoặc tắt tính năng lưu trữ ngoại tuyến:

  • Đối với nền tảng Android và Apple, tính năng duy trì ngoại tuyến được bật theo mặc định. Để vô hiệu hóa tính bền bỉ, hãy đặt tùy chọn PersistenceEnabled thành false .
  • Đối với web, tính năng duy trì ngoại tuyến bị tắt theo mặc định. Để kích hoạt tính bền bỉ, hãy gọi phương thức enablePersistence . Bộ nhớ cache của Cloud Firestore không tự động bị xóa giữa các phiên. Do đó, nếu ứng dụng web của bạn xử lý thông tin nhạy cảm, hãy đảm bảo hỏi người dùng xem họ có đang sử dụng thiết bị đáng tin cậy hay không trước khi bật tính năng kiên trì.

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
Nhanh
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và App Clip.
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
Mục tiêu-C
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và App Clip.
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));

Định cấu hình kích thước bộ đệm

Khi tính năng kiên trì được bật, Cloud Firestore sẽ lưu vào bộ nhớ cache mọi tài liệu nhận được từ chương trình phụ trợ để truy cập ngoại tuyến. Cloud Firestore đặt ngưỡng mặc định cho kích thước bộ đệm. Sau khi vượt quá mức mặc định, Cloud Firestore định kỳ cố gắng dọn sạch các tài liệu cũ hơn, không sử dụng. Bạn có thể định cấu hình ngưỡng kích thước bộ đệm khác hoặc tắt hoàn toàn quy trình dọn dẹp:

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
});
Nhanh
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và 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
// Set cache size to 1 MB
settings.cacheSettings = PersistentCacheSettings(sizeBytes: 1_000_000)
Firestore.firestore().settings = settings
Mục tiêu-C
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và 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;
// 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,
);

Nghe dữ liệu ngoại tuyến

Trong khi thiết bị ngoại tuyến, nếu bạn đã bật tính năng duy trì ngoại tuyến, người nghe của bạn sẽ nhận được các sự kiện nghe khi dữ liệu được lưu trong bộ nhớ cache cục bộ thay đổi. Bạn có thể nghe tài liệu, bộ sưu tập và truy vấn.

Để kiểm tra xem bạn đang nhận dữ liệu từ máy chủ hay bộ đệm ẩn, hãy sử dụng thuộc tính fromCache trên SnapshotMetadata trong sự kiện ảnh chụp nhanh của bạn. Nếu fromCachetrue thì dữ liệu đến từ bộ đệm và có thể cũ hoặc không đầy đủ. Nếu fromCachefalse , thì dữ liệu đã hoàn tất và hiện tại với các bản cập nhật mới nhất trên máy chủ.

Theo mặc định, không có sự kiện nào được đưa ra nếu chỉ có SnapshotMetadata thay đổi. Nếu bạn dựa vào các giá trị fromCache , hãy chỉ định tùy chọn nghe includeMetadataChanges khi bạn đính kèm trình xử lý nghe của mình.

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);
      });
  });
Nhanh
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và 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)")
}
Mục tiêu-C
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và 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);
    }];

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

Nhận dữ liệu ngoại tuyến

Nếu bạn nhận được tài liệu trong khi thiết bị ngoại tuyến, Cloud Firestore sẽ trả về dữ liệu từ bộ đệm.

Khi truy vấn một bộ sưu tập, một kết quả trống sẽ được trả về nếu không có tài liệu nào được lưu trong bộ nhớ cache. Thay vào đó, khi tìm nạp một tài liệu cụ thể, một lỗi sẽ được trả về.

Truy vấn dữ liệu ngoại tuyến

Truy vấn hoạt động với sự kiên trì ngoại tuyến. Bạn có thể truy xuất kết quả của các truy vấn bằng cách nhận trực tiếp hoặc bằng cách nghe, như được mô tả trong các phần trước. Bạn cũng có thể tạo các truy vấn mới trên dữ liệu được lưu trữ cục bộ khi thiết bị ngoại tuyến, nhưng các truy vấn ban đầu sẽ chỉ chạy đối với các tài liệu được lưu trong bộ nhớ cache.

Định cấu hình chỉ mục truy vấn ngoại tuyến

Theo mặc định, SDK Firestore quét tất cả tài liệu trong bộ sưu tập trong bộ nhớ đệm cục bộ khi thực hiện truy vấn ngoại tuyến. Với hành vi mặc định này, hiệu suất truy vấn ngoại tuyến có thể bị ảnh hưởng khi người dùng ngoại tuyến trong thời gian dài.

Bạn có thể cải thiện hiệu suất của truy vấn ngoại tuyến bằng cách định cấu hình chỉ mục truy vấn cục bộ:

Nhanh

SDK của nền tảng Apple cung cấp một phương thức setIndexConfiguration đọc cùng một cấu hình có cấu trúc JSON được sử dụng để định cấu hình các chỉ mục trên máy chủ, tuân theo cùng một định dạng định nghĩa chỉ mục .

// You will normally read this from a file asset or cloud storage.
let indexConfigJson = """
  {
    indexes: [
        ...
    ],
    fieldOverrides: [
        ...
    ]
  }
"""

// Apply the configuration.
Firestore.firestore().setIndexConfiguration(indexConfigJson)
Mục tiêu-C

SDK của nền tảng Apple cung cấp setIndexConfiguration - các phương thức đọc cùng một cấu hình có cấu trúc JSON được sử dụng để định cấu hình các chỉ mục trên máy chủ, tuân theo cùng một định dạng định nghĩa chỉ mục .

// 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

SDK Android cung cấp một phương thức setIndexConfiguration đọc cùng một cấu hình có cấu trúc JSON được sử dụng để định cấu hình các chỉ mục trên máy chủ, tuân theo cùng một định dạng định nghĩa chỉ mục .

// 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

SDK Android cung cấp một phương thức setIndexConfiguration đọc cùng một cấu hình có cấu trúc JSON được sử dụng để định cấu hình các chỉ mục trên máy chủ, tuân theo cùng một định dạng định nghĩa chỉ mục .

// You will normally read this from a file asset or cloud storage.
val indexConfigJson = """
{
  indexes: [
      ...
  ],
  fieldOverrides: [
      ...
  ]
}
"""

// Apply the configuration.
FirebaseFirestore.getInstance().setIndexConfiguration(indexConfigJson)

Dart

SDK Flutter cung cấp phương thức setIndexConfigurationFromJSON đọc cùng một cấu hình có cấu trúc JSON được sử dụng để định cấu hình các chỉ mục trên máy chủ, tuân theo cùng định dạng định nghĩa chỉ mục .

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

Ngoài ra, bạn có thể sử dụng phương thức setIndexConfiguration để định cấu hình các chỉ mục bằng API dựa trên lớp.

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

Cấu hình chỉ mục ngoại tuyến để sử dụng tùy thuộc vào bộ sưu tập và tài liệu mà ứng dụng của bạn truy cập nhiều khi ngoại tuyến và hiệu suất ngoại tuyến mà bạn muốn. Mặc dù bạn có thể xuất cấu hình chỉ mục phụ trợ của mình để sử dụng trên máy khách, nhưng các mẫu truy cập ngoại tuyến của ứng dụng của bạn có thể khác đáng kể so với các mẫu truy cập trực tuyến, vì vậy, cấu hình chỉ mục trực tuyến của bạn có thể không phù hợp để sử dụng ngoại tuyến. Những bộ sưu tập và tài liệu nào bạn muốn ứng dụng của mình truy cập ngoại tuyến với hiệu suất cao? Sau khi bạn đã phân tích hành vi của ứng dụng, hãy tuân theo các nguyên tắc xác định chỉ mục từ hướng dẫn lập chỉ mục .

Để cung cấp cấu hình chỉ mục ngoại tuyến để tải trong ứng dụng khách của bạn:

  • biên dịch và phân phối chúng với ứng dụng của bạn
  • tải xuống từ CDN
  • tìm nạp chúng từ một hệ thống lưu trữ như Cloud Storage cho Firebase .

Vô hiệu hóa và kích hoạt truy cập mạng

Bạn có thể sử dụng phương pháp bên dưới để tắt quyền truy cập mạng cho ứng dụng khách Cloud Firestore của mình. Trong khi truy cập mạng bị vô hiệu hóa, tất cả trình xử lý ảnh chụp nhanh và yêu cầu tài liệu sẽ truy xuất kết quả từ bộ đệm. Các thao tác ghi được xếp hàng đợi cho đến khi bật lại quyền truy cập mạng.

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
        // ...
    });
Nhanh
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và App Clip.
Firestore.firestore().disableNetwork { (error) in
    // Do offline things
    // ...
}
Mục tiêu-C
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và App Clip.
[[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
});

Sử dụng phương pháp sau để bật lại quyền truy cập mạng:

Web modular API

import { enableNetwork } from "firebase/firestore"; 

await enableNetwork(db);
// Do online actions
// ...

Web namespaced API

firebase.firestore().enableNetwork()
    .then(() => {
        // Do online actions
        // ...
    });
Nhanh
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và App Clip.
Firestore.firestore().enableNetwork { (error) in
    // Do online things
    // ...
}
Mục tiêu-C
Lưu ý: Sản phẩm này không khả dụng trên các mục tiêu watchOS và App Clip.
[[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
});