Odczyt i zapis danych w internecie

(Opcjonalnie) Prototypowanie i testowanie przy użyciu Firebase Local Emulator Suite

Zanim zaczniesz mówić o tym, jak Twoja aplikacja odczytuje dane z usługi Realtime Database i zapisuje w niej zapisy, przedstawimy zestaw narzędzi, które możesz wykorzystać do prototypowania i testowania funkcji Realtime Database funkcje: Firebase Local Emulator Suite. Jeśli testujesz inne dane optymalizowanie reguł zabezpieczeń lub wyszukiwanie opłacalny sposób interakcji z backendem, który daje możliwość pracy lokalnej bez wdrażania aktywnych usług może być świetnym pomysłem.

Emulator Realtime Database jest częścią Local Emulator Suite, która umożliwia aplikacji interakcję z emulowaną zawartością i konfiguracją bazy danych, a także opcjonalnie z emulowanymi zasobami projektu (funkcjami, innymi bazami danych i regułami zabezpieczeń).

Aby użyć emulatora Realtime Database, wystarczy wykonać kilka czynności:

  1. Dodajesz wiersz kodu do konfiguracji testowej aplikacji, aby połączyć się z emulatorem.
  2. Uruchomienie firebase emulators:start w katalogu głównym projektu lokalnego.
  3. Wykonywanie wywołań z prototypowego kodu aplikacji za pomocą platformy Realtime DatabaseSDK lub interfejsu API REST Realtime Database.

Dostępny jest szczegółowy przewodnik dotyczący elementów Realtime Database i Cloud Functions. Zapoznaj się też z Local Emulator Suitewprowadzeniem.

Pobieranie odniesienia do bazy danych

Aby móc odczytywać lub zapisywać dane z bazy danych, musisz mieć instancję firebase.database.Reference:

Web

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web

var database = firebase.database();

Zapisywanie danych

Ten dokument zawiera podstawowe informacje o pobieraniu danych oraz ich porządkowaniu i filtrowaniu. danych Firebase.

Dane Firebase są pobierane przez dołączenie odbiornika asynchronicznego do firebase.database.Reference Detektor jest aktywowany raz dla początkowym stanie danych i ponownie po każdej zmianie danych.

Podstawowe operacje zapisu

W przypadku podstawowych operacji zapisu możesz użyć funkcji set(), aby zapisać dane w określonym zastępując wszystkie istniejące dane w tej ścieżce. Na przykład kanał społecznościowy aplikacja do tworzenia blogów może dodać użytkownika z adresem set() w następujący sposób:

Web

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

function writeUserData(userId, name, email, imageUrl) {
  const db = getDatabase();
  set(ref(db, 'users/' + userId), {
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

Web

function writeUserData(userId, name, email, imageUrl) {
  firebase.database().ref('users/' + userId).set({
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

Użycie elementu set() powoduje zastąpienie danych w określonej lokalizacji, w tym danych podrzędnych węzłów.

Odczytywanie danych

Wykrywaj zdarzenia dotyczące wartości

Aby odczytywać dane na ścieżce i nasłuchiwać zmian, użyj funkcji onValue() w celu obserwacji zdarzeń. Za pomocą tego zdarzenia możesz odczytywać statyczne zrzuty zawartości ze ścieżki dostępnej w momencie wystąpienia zdarzenia. Ta metoda jest wyzwalana raz, gdy dołącza detektor, i ponownie za każdym razem, gdy dane, w tym elementy podrzędne, zostaną zmienione. Funkcja wywołania z okazji zdarzenia otrzymuje migawkę zawierającą wszystkie dane w danej lokalizacji, w tym dane podrzędne. W przypadku braku danych funkcja migawka zwróci wartość false, gdy wywołasz exists() i null w przypadku wywołania val().

W poniższym przykładzie pokazano, jak aplikacja do blogowania społecznościowego pobiera liczba gwiazdek posta z bazy danych:

Web

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

const db = getDatabase();
const starCountRef = ref(db, 'posts/' + postId + '/starCount');
onValue(starCountRef, (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

Web

var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

Detektor odbiera obiekt snapshot zawierający dane w określonym miejscu tej lokalizacji w bazie danych w momencie wystąpienia zdarzenia. Dane można pobrać w snapshot przy użyciu metody val().

Odczytaj dane raz

Jednorazowy odczyt danych za pomocą funkcji get()

Pakiet SDK umożliwia zarządzanie interakcjami z serwerami baz danych niezależnie od tego, czy aplikacja jest online, czy offline.

Ogólnie zalecamy używanie opisanych powyżej metod zdarzeń wartościowych do odczytywania danych, aby otrzymywać powiadomienia o ich aktualizacjach z back-endu. Techniki słuchania zmniejszają wykorzystanie i obciążenia, a także są optymalizowane pod kątem zapewnienia użytkownikom najlepszych wrażeń w sieci i poza nią.

Jeśli dane są potrzebne tylko raz, możesz użyć funkcji get(), aby uzyskać podsumowanie danych z bazy danych. Jeśli z jakiegokolwiek powodu get() nie może zwrócić wartości serwera, klient sprawdzi pamięć podręczną lokalnego magazynu i zwróci błąd, jeśli wartość nadal nie zostanie znaleziona.

Niepotrzebne użycie get() może zwiększyć wykorzystanie przepustowości i doprowadzić do utraty . Można temu zapobiec, korzystając z detektora w czasie rzeczywistym, jak pokazano powyżej.

Web

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

const dbRef = ref(getDatabase());
get(child(dbRef, `users/${userId}`)).then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

Web

const dbRef = firebase.database().ref();
dbRef.child("users").child(userId).get().then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

Jednorazowe odczytywanie danych z obserwatorem

W niektórych przypadkach możesz chcieć, aby wartość z lokalnej pamięci podręcznej była zwracana natychmiast zamiast sprawdzać zaktualizowaną wartość na serwerze. W możesz użyć funkcji once(), aby od razu pobrać dane z pamięci podręcznej dysku lokalnego.

Jest to przydatne w przypadku danych, które trzeba wczytać tylko raz i które nie powinny się często zmieniać ani wymagać aktywnego słuchania. Na przykład aplikacja do blogowania w poprzednich przykładach używa tej metody do wczytywania profilu użytkownika, gdy ten zaczyna pisać nowy post:

Web

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

const db = getDatabase();
const auth = getAuth();

const userId = auth.currentUser.uid;
return onValue(ref(db, '/users/' + userId), (snapshot) => {
  const username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
}, {
  onlyOnce: true
});

Web

var userId = firebase.auth().currentUser.uid;
return firebase.database().ref('/users/' + userId).once('value').then((snapshot) => {
  var username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
});

Aktualizowanie lub usuwanie danych

Zaktualizuj określone pola

Jednoczesne zapisywanie do określonych elementów podrzędnych węzła bez zastępowania innych węzłów podrzędnych, użyj metody update().

Wywołując update(), możesz zaktualizować wartości podrzędne niższego poziomu przez i podaj ścieżkę dostępu do klucza. Jeśli dane są przechowywane w wielu lokalizacjach na potrzeby skalowania możesz zaktualizować wszystkie wystąpienia tych danych za pomocą funkcji rozpowszechnianie danych.

Na przykład aplikacja do obsługi blogów społecznościowych może utworzyć posta i jednocześnie aktualizować do kanału ostatniej aktywności i kanału aktywności użytkownika publikującego za pomocą podobny do tego:

Web

import { getDatabase, ref, child, push, update } from "firebase/database";

function writeNewPost(uid, username, picture, title, body) {
  const db = getDatabase();

  // A post entry.
  const postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  const newPostKey = push(child(ref(db), 'posts')).key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  const updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return update(ref(db), updates);
}

Web

function writeNewPost(uid, username, picture, title, body) {
  // A post entry.
  var postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  var newPostKey = firebase.database().ref().child('posts').push().key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  var updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return firebase.database().ref().update(updates);
}

W tym przykładzie użyto metody push() do utworzenia w węźle posta posta zawierającego posty dla: wszystkich użytkowników w organizacji /posts/$postid i jednocześnie pobierają klucz. Następnie można użyć klucza do utworzenia drugiego wpisu w postach użytkownika na stronie /user-posts/$userid/$postid.

Korzystając z tych ścieżek, można wprowadzać zmiany w wielu lokalizacjach jednocześnie drzewo JSON z pojedynczym wywołaniem update(), jak w tym przykładzie utworzy nowy post w obu lokalizacjach. Równoczesne aktualizacje dokonywane w ten sposób są niepodzielne: wszystkie aktualizacje zakończą się sukcesem, albo wszystkie aktualizacje kończą się niepowodzeniem.

Dodaj zakończone wywołanie zwrotne

Jeśli chcesz się dowiedzieć, kiedy Twoje dane zostały zatwierdzone, możesz dodać zakończenia. Zarówno set(), jak i update() przyjmują opcjonalną funkcję zwracania wartości, która jest wywoływana po zaakceptowaniu zapisu w bazie danych. Jeśli wywołanie zakończyło się niepowodzeniem, wywołanie zwrotne otrzyma obiekt błędu wskazujący przyczynę niepowodzenia.

Web

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

const db = getDatabase();
set(ref(db, 'users/' + userId), {
  username: name,
  email: email,
  profile_picture : imageUrl
})
.then(() => {
  // Data saved successfully!
})
.catch((error) => {
  // The write failed...
});

Web

firebase.database().ref('users/' + userId).set({
  username: name,
  email: email,
  profile_picture : imageUrl
}, (error) => {
  if (error) {
    // The write failed...
  } else {
    // Data saved successfully!
  }
});

Usuń dane

Najprostszym sposobem usuwania danych jest wywołanie funkcji remove() z odniesieniem do lokalizacji tych danych.

Możesz również usunąć, określając null jako wartość innego zapisu operacji, na przykład set() lub update(). Tej metody możesz używać, za pomocą update(), aby usunąć wiele elementów podrzędnych w pojedynczym wywołaniu interfejsu API.

Otrzymano „Promise

Aby dowiedzieć się, kiedy dane są przekazywane na serwerze Firebase Realtime Database, może użyć polecenia Promise Zarówno set(), jak i update() mogą zwrócić wartość Promise, dzięki której dowiesz się, kiedy dla bazy danych jest zatwierdzany zapis.

Odłącz detektory

Wywołania zwrotne są usuwane przez wywołanie metody off() na Twoim Dokumentacja bazy danych Firebase.

Możesz usunąć pojedynczy detektor, przekazując go jako parametr do off(). Wywołanie funkcji off() w przypadku lokalizacji bez argumentów powoduje usunięcie wszystkich słuchaczy w tej lokalizacji.

Wywołanie metody off() na odbiorcy nadrzędnym nie powoduje automatycznego usuwania odbiorców zarejestrowanych na jego węzłach podrzędnych. Aby usunąć wywołania zwrotne, należy też wywołać metodę off() na wszystkich odbiorcach podrzędnych.

Zapisywanie danych jako transakcji

Podczas pracy z danymi, które mogły zostać uszkodzone w wyniku równoczesnej pracy takich jak liczniki przyrostowe, możesz użyć funkcji operacji transakcji. Możesz podać dla tej operacji funkcję aktualizacji i opcjonalne wywołanie zwrotne po zakończeniu. Funkcja aktualizacji przyjmuje bieżący stan danych jako argument i zwraca nowy pożądany stan, który chcesz zapisać. Jeśli zanim nowa wartość zostanie pomyślnie zapisana w lokalizacji, inny klient funkcja aktualizacji jest wywoływana ponownie z nową, bieżącą wartością, próba zapisu zostanie ponowiona.

Na przykład w przypadku aplikacji do prowadzenia bloga społecznościowego możesz zezwolić użytkownikom na oznaczanie postów gwiazdką i usuwanie tej oceny oraz śledzenie, ile gwiazdek otrzymał dany post.

Web

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

function toggleStar(uid) {
  const db = getDatabase();
  const postRef = ref(db, '/posts/foo-bar-123');

  runTransaction(postRef, (post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

Web

function toggleStar(postRef, uid) {
  postRef.transaction((post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

Użycie transakcji zapobiega nieprawidłowemu liczeniu gwiazdek w przypadku większej liczby Użytkownicy oznaczali gwiazdką tego samego posta w tym samym czasie lub klient miał nieaktualne dane. Jeśli transakcja została odrzucona, serwer zwraca bieżącą wartość klientowi, co powoduje ponowne uruchomienie transakcji zaktualizowanej wartości. Powtarza się do momentu zaakceptowania transakcji lub jej przerwania transakcji.

Atomic przyrosty po stronie serwera

W powyższym przypadku użycia zapisujemy w bazie danych 2 wartości: identyfikator użytkownik, który oznacza post lub oznaczenie gwiazdką posta, i zwiększona liczba gwiazdek. Jeśli że użytkownik oznaczy post gwiazdką, możemy użyć przyrostu atomowego zamiast transakcji.

Web

function addStar(uid, key) {
  import { getDatabase, increment, ref, update } from "firebase/database";
  const dbRef = ref(getDatabase());

  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = increment(1);
  update(dbRef, updates);
}

Web

function addStar(uid, key) {
  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  firebase.database().ref().update(updates);
}

Ten kod nie korzysta z operacji transakcji, więc nie pobiera automatycznie w przypadku wystąpienia konfliktu aktualizacji uruchom ponownie system. Ponieważ jednak operacja przyrostu odbywa się bezpośrednio na serwerze bazy danych, nie występuje konflikt.

Jeśli chcesz wykrywać i odrzucać konflikty związane z konkretną aplikacją, np. gdy użytkownik wyróżnia wpis, który wyróżnił już wcześniej, na potrzeby tego przypadku użycia napisz niestandardowe reguły bezpieczeństwa.

Praca z danymi w trybie offline

Jeśli klient utraci połączenie sieciowe, aplikacja będzie nadal działać .

Każdy klient połączony z bazą danych Firebase ma własną wersję wewnętrzną wszystkich aktywnych danych. Dane są najpierw zapisywane w tej wersji lokalnej. Następnie klient Firebase synchronizuje te dane ze zdalnymi serwerami bazy danych i z innymi klientami według zasady „najlepszego wysiłku”.

W rezultacie wszystkie zapisy w bazie danych wywołują zdarzenia lokalne natychmiast, przed jakiekolwiek dane są zapisywane na serwerze. Oznacza to, że aplikacja pozostaje niezależnie od opóźnienia sieciowego czy połączenia.

Po ponownym nawiązaniu połączenia aplikacja otrzymuje odpowiedni zestaw zdarzeń, dzięki czemu klient synchronizuje się z bieżącym stanem serwera, bez konieczności pisania kodu niestandardowego.

Więcej informacji o działaniu offline znajdziesz w artykule Więcej informacji o możliwościach online i offline.

Dalsze kroki