如何使用 Firebase 将初始数据加载与增量 children 分开?

How to separate initial data load from incremental children with Firebase?

我有一个应用程序,每 5 秒左右就会向 Firebase 添加新的 children。我有几千个children.

在应用程序加载时,我希望以不同于后续 children 每 5 秒滴流的方式处理最初的数千个。

您可能会建议我使用值,处理所有内容,然后使用 children_added。但我相信,如果处理时间过长,我可能会漏掉一点。

有没有一种方法可以在 Firebase 中做到这一点,保证我不会错过任何一点?

这个问题有两种可能的解决方案,都不令人满意:

1) 为您的 children 加上时间戳,并且只请求时间戳大于 "now" 值的 children。这不是很好,因为您的应用程序 "now" 和 "now" 之间可能存在同步问题,无论是将数据推送到 Firebase 还是 "now".[=10= 的 Firebase 服务器值]

2) 使用值并添加 child。在 child_added 中看到的任何已在值中看到的重复项可能会被丢弃。这对于大型数据集是不可用的,因为它需要下载所有历史数据两次!

为什么不能有像 "child_added" 这样的命令,它永远不会提供所有信息?

由于 child_added 初始预加载数据的事件将在 之前触发 value 事件,您可以使用 value 事件作为一种 "initial data loaded" 通知。这是我从 another similar Whosebug question.

稍微修改的一些代码
var initialDataLoaded = false;
var ref = new Firebase('https://<your-Firebase>.firebaseio.com');

ref.on('child_added', function(snapshot) {
  if (initialDataLoaded) {
    var msg = snapshot.val().msg;
    // do something here
  } else {
    // we are ignoring this child since it is pre-existing data
  }
});

ref.once('value', function(snapshot) {
  initialDataLoaded = true;
});

值得庆幸的是,Firebase 会巧妙地缓存此数据,这意味着同时创建 child_addedvalue 侦听器只会下载一次数据。为已经通过网络的数据添加新的 Firebase 侦听器非常便宜,您应该会觉得经常这样做很舒服。

如果您担心在实际上不需要时下载所有初始数据,我会按照@FrankvanPuffelen 在评论中的建议使用带时间戳的查询。这非常有效,并使用 Firebase 查询进行了优化。

改进了@jacobawenger 的答案以不使用全局状态变量

var ref = new Firebase('https://<your-Firebase>.firebaseio.com');

ref.once('value', function(snapshot) {
  // do something here with initial value

  ref.on('child_added', function(snapshot) {
    // do something here with added childs
  });

});

在数据库中添加了一个 createdAt 时间戳,并用当前时间限制了 child_added,这对我来说似乎工作正常。

const onChildAdded = database()
  .ref(refURL)
  .limitToLast(constValues.LAST_MESSAGE_ADDED)
  .orderByChild('createdAt')
  .startAt(date.getTime())
  .on('child_added', (snapshot) => {
    parseMessage(snapshot);
  });

database()
  .ref(refURL)
  .limitToLast(constValues.LAST_MESSAGES_LIMIT)
  .once('value', (snapshot) => {
    parseInitialMessages(snapshot);
    setLoading(false);
  });