使用 Cloud Firestore 获取实时更新

您可以使用 onSnapshot() 方法侦听一个文档。对您提供的回调函数的初始调用将使用相应文档的当前内容立即创建一份文档快照。之后,每次内容发生更改时,另一次调用将更新该文档快照。

网页
db.collection("cities").doc("SF")
    .onSnapshot(function(doc) {
        console.log("Current data: ", doc.data());
    });
Swift
db.collection("cities").document("SF")
    .addSnapshotListener { documentSnapshot, error in
      guard let document = documentSnapshot else {
        print("Error fetching document: \(error!)")
        return
      }
      print("Current data: \(document.data())")
    }
Objective-C
[[[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]
    addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error fetching document: %@", error);
        return;
      }
      NSLog(@"Current data: %@", snapshot.data);
    }];
  
Android
final DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
    @Override
    public void onEvent(@Nullable DocumentSnapshot snapshot,
                        @Nullable FirebaseFirestoreException e) {
        if (e != null) {
            Log.w(TAG, "Listen failed.", e);
            return;
        }

        if (snapshot != null && snapshot.exists()) {
            Log.d(TAG, "Current data: " + snapshot.getData());
        } else {
            Log.d(TAG, "Current data: null");
        }
    }
});
Java
DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
  @Override
  public void onEvent(@Nullable DocumentSnapshot snapshot,
                      @Nullable FirestoreException e) {
    if (e != null) {
      System.err.println("Listen failed: " + e);
      return;
    }

    if (snapshot != null && snapshot.exists()) {
      System.out.println("Current data: " + snapshot.getData());
    } else {
      System.out.print("Current data: null");
    }
  }
});
Python
# Not yet supported in Python client library
Node.js
var doc = db.collection('cities').doc('SF');

var observer = doc.onSnapshot(docSnapshot => {
  console.log(`Received doc snapshot: ${docSnapshot}`);
  // ...
}, err => {
  console.log(`Encountered error: ${err}`);
});
Go
// Not yet supported in Go client library
PHP
// Not yet supported in PHP client library

本地更改事件

您应用中的本地写入将立即调用快照侦听器。这是因为一项称为“延迟补偿”的重要功能。执行写操作时,在数据发送到后端之前,系统会立即通知侦听器将有新数据。

检索的文档具有一个 metadata.hasPendingWrites 属性,指示文档是否存在尚未写入后端的本地更改。您可以使用此属性来确定快照侦听器接收到的事件的来源:

网页
db.collection("cities").doc("SF")
    .onSnapshot(function(doc) {
        var source = doc.metadata.hasPendingWrites ? "Local" : "Server";
        console.log(source, " data: ", doc.data());
    });
Swift
db.collection("cities").document("SF")
    .addSnapshotListener { documentSnapshot, error in
        guard let document = documentSnapshot else {
            print("Error fetching document: \(error!)")
            return
        }
        let source = document.metadata.hasPendingWrites ? "Local" : "Server"
        print("\(source) data: \(document.data())")
    }
Objective-C
[[[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]
    addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error fetching document: %@", error);
        return;
      }
      NSString *source = snapshot.metadata.hasPendingWrites ? @"Local" : @"Server";
      NSLog(@"%@ data: %@", source, snapshot.data);
    }];
  
Android
final DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
    @Override
    public void onEvent(@Nullable DocumentSnapshot snapshot,
                        @Nullable FirebaseFirestoreException e) {
        if (e != null) {
            Log.w(TAG, "Listen failed.", e);
            return;
        }

        String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
                ? "Local" : "Server";

        if (snapshot != null && snapshot.exists()) {
            Log.d(TAG, source + " data: " + snapshot.getData());
        } else {
            Log.d(TAG, source + " data: null");
        }
    }
});
Java
# Not yet supported in the Java client library
Python
# Not yet supported in Python client library
Node.js
// Not yet supported in the Node.js client library
Go
// Not yet supported in Go client library
PHP
// Not yet supported in PHP client library

元数据更改事件

在侦听对文档、集合或查询的更改时,您可以传递选项来控制侦听器将接收到的事件的粒度。

默认情况下,系统不会就仅影响元数据的变更通知侦听器。考虑当您的应用写入新文档时会发生什么情况:

  1. 新数据立即触发更改事件。该文档尚未写入后端,因此“待写入”标志为 true
  2. 文档被写入后端。
  3. 成功写入后,后端通知客户端。文档数据没有变化,但因为“待写入”标志现在为 false,产生了一项元数据更改。

如果您希望在文档或查询元数据发生更改时均收到快照事件,请在连接侦听器时传递侦听选项对象:

网页
db.collection("cities").doc("SF")
    .onSnapshot({
        // Listen for document metadata changes
        includeMetadataChanges: true
    }, function(doc) {
        // ...
    });
Swift
// Listen to document metadata.
let options = DocumentListenOptions().includeMetadataChanges(true);

db.collection("cities").document("SF")
    .addSnapshotListener(options: options) { documentSnapshot, error in
        // ...
    }
Objective-C
// Listen for metadata changes.
FIRDocumentListenOptions *options = [[FIRDocumentListenOptions init] includeMetadataChanges:true]

[[[[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]
 addSnapshotListenerWithOptions:options listener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
   // ...
 }]];
Android
// Listen for metadata changes to the document.
DocumentListenOptions options = new DocumentListenOptions()
        .includeMetadataChanges();

DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(options, new EventListener<DocumentSnapshot>() {
    @Override
    public void onEvent(@Nullable DocumentSnapshot snapshot,
                        @Nullable FirebaseFirestoreException e) {
        // ...
    }
});
Java
# Not yet supported in the Java client library
Python
# Not yet supported in Python client library
Node.js
// Not yet supported the Node.js client library
Go
// Not yet supported in Go client library
PHP
// Not yet supported in PHP client library

侦听集合中的多个文档

与文档一样,您可以使用 onSnapshot() 而不是 get() 来侦听查询结果。这将创建一个查询快照。例如,要侦听状态为 CA 的文档:

网页
db.collection("cities").where("state", "==", "CA")
    .onSnapshot(function(querySnapshot) {
        var cities = [];
        querySnapshot.forEach(function(doc) {
            cities.push(doc.data().name);
        });
        console.log("Current cities in CA: ", cities.join(", "));
    });
Swift
db.collection("cities").whereField("state", isEqualTo: "CA")
    .addSnapshotListener { querySnapshot, error in
        guard let documents = querySnapshot?.documents else {
            print("Error fetching documents: \(error!)")
            return
        }
        let cities = documents.map { $0["name"]! }
        print("Current cities in CA: \(cities)")
    }
Objective-C
[[[self.db collectionWithPath:@"cities"] queryWhereField:@"state" isEqualTo:@"CA"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error fetching documents: %@", error);
        return;
      }
      NSMutableArray *cities = [NSMutableArray array];
      for (FIRDocumentSnapshot *document in snapshot.documents) {
        [cities addObject:document.data[@"name"]];
      }
      NSLog(@"Current cities in CA: %@", cities);
    }];
  
Android
db.collection("cities")
        .whereEqualTo("state", "CA")
        .addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot value,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w(TAG, "Listen failed.", e);
                    return;
                }

                List<String> cities = new ArrayList<>();
                for (QueryDocumentSnapshot doc : value) {
                    if (doc.get("name") != null) {
                        cities.add(doc.getString("name"));
                    }
                }
                Log.d(TAG, "Current cites in CA: " + cities);
            }
        });
Java
db.collection("cities")
    .whereEqualTo("state", "CA")
    .addSnapshotListener(new EventListener<QuerySnapshot>() {
      @Override
      public void onEvent(@Nullable QuerySnapshot snapshots,
                          @Nullable FirestoreException e) {
        if (e != null) {
          System.err.println("Listen failed:" + e);
          return;
        }

        List<String> cities = new ArrayList<>();
        for (DocumentSnapshot doc : snapshots) {
          if (doc.get("name") != null) {
            cities.add(doc.getString("name"));
          }
        }
        System.out.println("Current cites in CA: " + cities);
      }
    });
Python
# Not yet supported in Python client library
Node.js
var query = db.collection('cities').where('state', '==', 'CA');

var observer = query.onSnapshot(querySnapshot => {
  console.log(`Received query snapshot of size ${querySnapshot.size}`);
  // ...
}, err => {
  console.log(`Encountered error: ${err}`);
});
Go
// Not yet supported in Go client library
PHP
// Not yet supported in PHP client library

每当查询结果发生更改时(即添加、删除或修改文档时),快照处理程序将收到一个新的查询快照。

查看快照之间的更改

查看不同的查询快照之间发生的查询结果实际更改(而非简单地使用整个查询快照)通常很有用。例如,您可能希望在添加、删除和修改单个文档时保留缓存。

网页
db.collection("cities").where("state", "==", "CA")
    .onSnapshot(function(snapshot) {
        snapshot.docChanges.forEach(function(change) {
            if (change.type === "added") {
                console.log("New city: ", change.doc.data());
            }
            if (change.type === "modified") {
                console.log("Modified city: ", change.doc.data());
            }
            if (change.type === "removed") {
                console.log("Removed city: ", change.doc.data());
            }
        });
    });
Swift
db.collection("cities").whereField("state", isEqualTo: "CA")
    .addSnapshotListener { querySnapshot, error in
        guard let snapshot = querySnapshot else {
            print("Error fetching snapshots: \(error!)")
            return
        }
        snapshot.documentChanges.forEach { diff in
            if (diff.type == .added) {
                print("New city: \(diff.document.data())")
            }
            if (diff.type == .modified) {
                print("Modified city: \(diff.document.data())")
            }
            if (diff.type == .removed) {
                print("Removed city: \(diff.document.data())")
            }
        }
    }
Objective-C
[[[self.db collectionWithPath:@"cities"] queryWhereField:@"state" isEqualTo:@"CA"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error fetching documents: %@", error);
        return;
      }
      for (FIRDocumentChange *diff in snapshot.documentChanges) {
        if (diff.type == FIRDocumentChangeTypeAdded) {
          NSLog(@"New city: %@", diff.document.data);
        }
        if (diff.type == FIRDocumentChangeTypeModified) {
          NSLog(@"Modified city: %@", diff.document.data);
        }
        if (diff.type == FIRDocumentChangeTypeRemoved) {
          NSLog(@"Removed city: %@", diff.document.data);
        }
      }
    }];
  
Android
db.collection("cities")
        .whereEqualTo("state", "CA")
        .addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot snapshots,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w(TAG, "listen:error", e);
                    return;
                }

                for (DocumentChange dc : snapshots.getDocumentChanges()) {
                    switch (dc.getType()) {
                        case ADDED:
                            Log.d(TAG, "New city: " + dc.getDocument().getData());
                            break;
                        case MODIFIED:
                            Log.d(TAG, "Modified city: " + dc.getDocument().getData());
                            break;
                        case REMOVED:
                            Log.d(TAG, "Removed city: " + dc.getDocument().getData());
                            break;
                    }
                }

            }
        });
Java
db.collection("cities")
    .whereEqualTo("state", "CA")
    .addSnapshotListener(new EventListener<QuerySnapshot>() {
      @Override
      public void onEvent(@Nullable QuerySnapshot snapshots,
                          @Nullable FirestoreException e) {
        if (e != null) {
          System.err.println("Listen failed: " + e);
          return;
        }

        for (DocumentChange dc : snapshots.getDocumentChanges()) {
          switch (dc.getType()) {
            case ADDED:
              System.out.println("New city: " + dc.getDocument().getData());
              break;
            case MODIFIED:
              System.out.println("Modified city: " + dc.getDocument().getData());
              break;
            case REMOVED:
              System.out.println("Removed city: " + dc.getDocument().getData());
              break;
            default:
              break;
          }
        }
      }
    });
Python
# Not yet supported in the Python client library
Node.js
// Not yet supported in the Node.js client library
Go
// Not yet supported in Go client library
PHP
// Not yet supported in PHP client library

初始状态可以直接来自服务器,也可以来自本地缓存。如果在本地缓存中有可用的状态,则查询快照最初将使用缓存的数据填充,并在客户端与服务器状态一致时使用服务器数据进行更新。

分离侦听器

当不再需要侦听数据时,您必须分离侦听器,以便停止调用事件回调函数。这样客户端可以停止使用带宽来接收更新。您可以在 onSnapshot() 上使用 unsubscribe 函数来停止侦听更新。

网页
var unsubscribe = db.collection("cities")
    .onSnapshot(function () {});
// ...
// Stop listening to changes
unsubscribe();
Swift
let listener = db.collection("cities").addSnapshotListener { querySnapshot, error in
    // ...
}

// ...

// Stop listening to changes
listener.remove()
Objective-C
id<FIRListenerRegistration> listener = [[self.db collectionWithPath:@"cities"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      // ...
}];

// ...

// Stop listening to changes
[listener remove];
  
Android
Query query = db.collection("cities");
ListenerRegistration registration = query.addSnapshotListener(
        new EventListener<QuerySnapshot>() {
            // ...
        });

// ...

// Stop listening to changes
registration.remove();
Java
Query query = db.collection("cities");
ListenerRegistration registration = query.addSnapshotListener(
    new EventListener<QuerySnapshot>() {
      // ...
    });

// ...

// Stop listening to changes
registration.remove();
Python
# Not yet supported in the Python client library
Node.js
var unsub = db.collection('cities').onSnapshot(() => {});

// ...

// Stop listening for changes
unsub();
Go
// Not yet supported in Go client library
PHP
// Not yet supported in PHP client library

处理侦听错误

侦听有时候可能会失败 - 可能的原因有很多,例如:安全权限、您在尝试侦听无效的查询。(详细了解有效和无效查询。)要处理这些失败,您可以在关联快照侦听器时提供错误回调函数。发生错误后,侦听器将不再收到任何事件,因此无需分离侦听器。

网页
db.collection("cities")
    .onSnapshot(function(snapshot) {
        //...
    }, function(error) {
        //...
    });
Swift
db.collection("cities")
    .addSnapshotListener { querySnapshot, error in
        if let error = error {
            print("Error retreiving collection: \(error)")
        }
    }
Objective-C
[[self.db collectionWithPath:@"cities"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      if (error != nil) {
        NSLog(@"Error retreving collection: %@", error);
      }
    }];
  
Android
db.collection("cities")
        .addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot snapshots,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w(TAG, "listen:error", e);
                    return;
                }

                for (DocumentChange dc : snapshots.getDocumentChanges()) {
                    if (dc.getType() == Type.ADDED) {
                        Log.d(TAG, "New city: " + dc.getDocument().getData());
                    }
                }

            }
        });
Java
db.collection("cities")
    .addSnapshotListener(new EventListener<QuerySnapshot>() {
      @Override
      public void onEvent(@Nullable QuerySnapshot snapshots,
                          @Nullable FirestoreException e) {
        if (e != null) {
          System.err.println("Listen failed: " + e);
          return;
        }

        for (DocumentChange dc : snapshots.getDocumentChanges()) {
          if (dc.getType() == Type.ADDED) {
            System.out.println("New city: " + dc.getDocument().getData());
          }
        }
      }
    });
Python
# Not yet supported in the Python client library
Node.js
db.collection('cities')
    .onSnapshot((snapshot) => {
      //...
    }, (error) => {
      //...
    });
Go
// Not yet supported in Go client library
PHP
// Not yet supported in PHP client library

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面