获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

在 JavaScript 中启用离线功能

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

即使您的应用暂时失去网络连接,Firebase 应用也能正常工作。我们提供了几种工具来监视存在和同步本地状态与服务器状态,这些在本文档中进行了介绍。

管理存在

在实时应用程序中,检测客户端何时连接和断开连接通常很有用。例如,您可能希望在客户端断开连接时将用户标记为“离线”。

Firebase 数据库客户端提供简单的原语,当客户端与 Firebase 数据库服务器断开连接时,您可以使用这些原语写入数据库。无论客户端是否完全断开连接,这些更新都会发生,因此即使连接断开或客户端崩溃,您也可以依靠它们来清理数据。所有写入操作,包括设置、更新和删除,都可以在断开连接时执行。

这是一个使用onDisconnect原语在断开连接时写入数据的简单示例:

Web version 9

import { getDatabase, ref, onDisconnect } from "firebase/database";

const db = getDatabase();
const presenceRef = ref(db, "disconnectmessage");
// Write a string when this client loses connection
onDisconnect(presenceRef).set("I disconnected!");

Web version 8

var presenceRef = firebase.database().ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");

onDisconnect 的工作原理

当您建立onDisconnect()操作时,该操作位于 Firebase 实时数据库服务器上。服务器检查安全性以确保用户可以执行请求的写入事件,并在它无效时通知您的应用程序。然后服务器监视连接。如果在任何时候连接超时,或者被实时数据库客户端主动关闭,服务器会再次检查安全性(以确保操作仍然有效),然后调用事件。

您的应用可以在写操作上使用回调来确保正确附加了onDisconnect

Web version 9

onDisconnect(presenceRef).remove().catch((err) => {
  if (err) {
    console.error("could not establish onDisconnect event", err);
  }
});

Web version 8

presenceRef.onDisconnect().remove((err) => {
  if (err) {
    console.error("could not establish onDisconnect event", err);
  }
});

onDisconnect事件也可以通过调用.cancel()来取消:

Web version 9

const onDisconnectRef = onDisconnect(presenceRef);
onDisconnectRef.set("I disconnected");
// some time later when we change our minds
onDisconnectRef.cancel();

Web version 8

var onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.set("I disconnected");
// some time later when we change our minds
onDisconnectRef.cancel();

检测连接状态

对于许多与状态相关的功能,让您的应用知道它何时在线或离线很有用。 Firebase 实时数据库在/.info/connected提供了一个特殊位置,每次 Firebase 实时数据库客户端的连接状态更改时都会更新该位置。这是一个例子:

Web version 9

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const connectedRef = ref(db, ".info/connected");
onValue(connectedRef, (snap) => {
  if (snap.val() === true) {
    console.log("connected");
  } else {
    console.log("not connected");
  }
});

Web version 8

var connectedRef = firebase.database().ref(".info/connected");
connectedRef.on("value", (snap) => {
  if (snap.val() === true) {
    console.log("connected");
  } else {
    console.log("not connected");
  }
});

/.info/connected是一个布尔值,在实时数据库客户端之间不同步,因为该值取决于客户端的状态。换句话说,如果一个客户端将/.info/connected读取为 false,这并不能保证单独的客户端也会读取 false。

处理延迟

服务器时间戳

Firebase 实时数据库服务器提供了一种将服​​务器上生成的时间戳作为数据插入的机制。此功能与onDisconnect相结合,提供了一种简单的方法来可靠地记录实时数据库客户端断开连接的时间:

Web version 9

import { getDatabase, ref, onDisconnect, serverTimestamp } from "firebase/database";

const db = getDatabase();
const userLastOnlineRef = ref(db, "users/joe/lastOnline");
onDisconnect(userLastOnlineRef).set(serverTimestamp());

Web version 8

var userLastOnlineRef = firebase.database().ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);

时钟偏差

虽然firebase.database.ServerValue.TIMESTAMP更准确,并且对于大多数读/写操作更可取,但有时估计客户端相对于 Firebase 实时数据库服务器的时钟偏差可能很有用。您可以将回调附加到位置/.info/serverTimeOffset以获取 Firebase 实时数据库客户端添加到本地报告时间(以毫秒为单位的纪元时间)以估计服务器时间的值(以毫秒为单位)。请注意,此偏移的准确性可能会受到网络延迟的影响,因此主要用于发现时钟时间的大(> 1 秒)差异。

Web version 9

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const offsetRef = ref(db, ".info/serverTimeOffset");
onValue(offsetRef, (snap) => {
  const offset = snap.val();
  const estimatedServerTimeMs = new Date().getTime() + offset;
});

Web version 8

var offsetRef = firebase.database().ref(".info/serverTimeOffset");
offsetRef.on("value", (snap) => {
  var offset = snap.val();
  var estimatedServerTimeMs = new Date().getTime() + offset;
});

样本存在应用程序

通过将断开连接操作与连接状态监控和服务器时间戳相结合,您可以构建用户呈现系统。在此系统中,每个用户都将数据存储在数据库位置,以指示实时数据库客户端是否在线。客户端在联机时将此位置设置为 true,并在断开连接时设置时间戳。此时间戳指示给定用户的最后一次在线时间。

请注意,您的应用应在将用户标记为在线之前将断开连接操作排队,以避免在两个命令都可以发送到服务器之前客户端的网络连接丢失的情况下出现任何竞争条件。

这是一个简单的用户存在系统:

Web version 9

import { getDatabase, ref, onValue, push, onDisconnect, set, serverTimestamp } from "firebase/database";

// Since I can connect from multiple devices or browser tabs, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
const db = getDatabase();
const myConnectionsRef = ref(db, 'users/joe/connections');

// stores the timestamp of my last disconnect (the last time I was seen online)
const lastOnlineRef = ref(db, 'users/joe/lastOnline');

const connectedRef = ref(db, '.info/connected');
onValue(connectedRef, (snap) => {
  if (snap.val() === true) {
    // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect)
    const con = push(myConnectionsRef);

    // When I disconnect, remove this device
    onDisconnect(con).remove();

    // Add this device to my connections list
    // this value could contain info about the device or a timestamp too
    set(con, true);

    // When I disconnect, update the last time I was seen online
    onDisconnect(lastOnlineRef).set(serverTimestamp());
  }
});

Web version 8

// Since I can connect from multiple devices or browser tabs, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
var myConnectionsRef = firebase.database().ref('users/joe/connections');

// stores the timestamp of my last disconnect (the last time I was seen online)
var lastOnlineRef = firebase.database().ref('users/joe/lastOnline');

var connectedRef = firebase.database().ref('.info/connected');
connectedRef.on('value', (snap) => {
  if (snap.val() === true) {
    // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect)
    var con = myConnectionsRef.push();

    // When I disconnect, remove this device
    con.onDisconnect().remove();

    // Add this device to my connections list
    // this value could contain info about the device or a timestamp too
    con.set(true);

    // When I disconnect, update the last time I was seen online
    lastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);
  }
});