(Opcjonalnie) Prototypuj i testuj za pomocą Firebase Local Emulator Suite
Zanim omówimy sposób, w jaki Twoja aplikacja odczytuje i zapisuje w Bazie danych czasu rzeczywistego, przedstawmy zestaw narzędzi, których możesz użyć do prototypowania i testowania funkcjonalności Bazy danych czasu rzeczywistego: Firebase Local Emulator Suite. Jeśli wypróbowujesz różne modele danych, optymalizujesz reguły bezpieczeństwa lub pracujesz nad znalezieniem najbardziej opłacalnego sposobu interakcji z zapleczem, możliwość pracy lokalnej bez wdrażania usług na żywo może być świetnym pomysłem.
Emulator bazy danych czasu rzeczywistego jest częścią pakietu Local Emulator Suite, który umożliwia aplikacji interakcję z emulowaną zawartością i konfiguracją bazy danych, a także opcjonalnie emulowanymi zasobami projektu (funkcjami, innymi bazami danych i regułami bezpieczeństwa).
Korzystanie z emulatora bazy danych czasu rzeczywistego obejmuje tylko kilka kroków:
- Dodanie wiersza kodu do konfiguracji testowej aplikacji w celu nawiązania połączenia z emulatorem.
- Z katalogu głównego lokalnego katalogu projektu, uruchamiając
firebase emulators:start
. - Wykonywanie wywołań z kodu prototypu Twojej aplikacji przy użyciu pakietu SDK platformy Realtime Database w zwykły sposób lub przy użyciu interfejsu API REST bazy danych czasu rzeczywistego.
Dostępny jest szczegółowy przewodnik dotyczący bazy danych czasu rzeczywistego i funkcji chmury . Warto również zapoznać się z wprowadzeniem do pakietu Local Emulator Suite .
Uzyskaj odniesienie do bazy danych
Aby odczytywać lub zapisywać dane z bazy danych, potrzebujesz instancji firebase.database.Reference
:
Web modular API
import { getDatabase } from "firebase/database"; const database = getDatabase();
Web namespaced API
var database = firebase.database();
Zapisz dane
W tym dokumencie opisano podstawy pobierania danych oraz sposób porządkowania i filtrowania danych Firebase.
Dane Firebase są pobierane przez dołączenie asynchronicznego odbiornika do firebase.database.Reference
. Odbiornik jest uruchamiany raz dla początkowego stanu danych i ponownie za każdym razem, gdy dane się zmieniają.
Podstawowe operacje zapisu
W przypadku podstawowych operacji zapisu można użyć set()
w celu zapisania danych do określonego odwołania, zastępując wszelkie istniejące dane w tej ścieżce. Na przykład aplikacja do blogowania społecznościowego może dodać użytkownika za pomocą set()
w następujący sposób:
Web modular API
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 namespaced API
function writeUserData(userId, name, email, imageUrl) { firebase.database().ref('users/' + userId).set({ username: name, email: email, profile_picture : imageUrl }); }
Użycie metody set()
nadpisuje dane w określonej lokalizacji, w tym wszelkie węzły potomne.
Odczyt danych
Słuchaj wartościowych wydarzeń
Aby odczytać dane na ścieżce i nasłuchiwać zmian, użyj onValue()
do obserwacji zdarzeń. Tego zdarzenia można użyć do odczytania statycznych migawek zawartości w danej ścieżce, tak jak istniały one w momencie zdarzenia. Ta metoda jest uruchamiana raz, gdy odbiornik jest podłączony, i ponownie za każdym razem, gdy zmieniają się dane, w tym elementy podrzędne. Wywołanie zwrotne zdarzenia przekazuje migawkę zawierającą wszystkie dane w tej lokalizacji, w tym dane podrzędne. Jeśli nie ma żadnych danych, migawka zwróci false
, gdy wywołasz na niej exists()
i null
, gdy wywołasz na niej val()
.
Poniższy przykład ilustruje aplikację do blogowania społecznościowego pobierającą liczbę gwiazdek posta z bazy danych:
Web modular API
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 namespaced API
var starCountRef = firebase.database().ref('posts/' + postId + '/starCount'); starCountRef.on('value', (snapshot) => { const data = snapshot.val(); updateStarCount(postElement, data); });
Odbiornik odbiera snapshot
zawierającą dane w określonej lokalizacji w bazie danych w czasie zdarzenia. Możesz pobrać dane z snapshot
za pomocą metody val()
.
Odczytaj dane raz
Odczytaj dane raz za pomocą get()
Zestaw SDK jest przeznaczony do zarządzania interakcjami z serwerami baz danych niezależnie od tego, czy aplikacja jest w trybie online, czy offline.
Ogólnie rzecz biorąc, powinieneś używać opisanych powyżej technik zdarzeń wartości, aby odczytywać dane i otrzymywać powiadomienia o aktualizacjach danych z zaplecza. Techniki nasłuchiwania zmniejszają użycie i rozliczenia oraz są zoptymalizowane, aby zapewnić użytkownikom najlepsze wrażenia podczas korzystania z Internetu i offline.
Jeśli potrzebujesz danych tylko raz, możesz użyć funkcji get()
, aby uzyskać migawkę danych z bazy danych. Jeśli z jakiegokolwiek powodu get()
nie może zwrócić wartości serwera, klient sprawdzi lokalną pamięć podręczną i zwróci błąd, jeśli wartość nadal nie zostanie znaleziona.
Niepotrzebne użycie funkcji get()
może zwiększyć wykorzystanie przepustowości i doprowadzić do utraty wydajności, czemu można zapobiec, używając odbiornika działającego w czasie rzeczywistym, jak pokazano powyżej.
Web modular API
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 namespaced API
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); });
Odczytaj dane raz z obserwatorem
W niektórych przypadkach możesz chcieć, aby wartość z lokalnej pamięci podręcznej została zwrócona natychmiast, zamiast sprawdzania zaktualizowanej wartości na serwerze. W takich przypadkach możesz użyć once()
w celu natychmiastowego pobrania danych z lokalnej pamięci podręcznej dysku.
Jest to przydatne w przypadku danych, które muszą zostać załadowane tylko raz i nie oczekuje się, że będą się często zmieniać ani wymagać aktywnego słuchania. Na przykład aplikacja do blogowania w poprzednich przykładach używa tej metody do załadowania profilu użytkownika, gdy rozpoczyna on tworzenie nowego posta:
Web modular API
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 namespaced API
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
Aby jednocześnie zapisywać do określonych węzłów potomnych bez nadpisywania innych węzłów potomnych, należy użyć metody update()
.
Wywołując funkcję update()
, możesz zaktualizować wartości potomne niższego poziomu, określając ścieżkę dla klucza. Jeśli dane są przechowywane w wielu lokalizacjach w celu lepszego skalowania, możesz zaktualizować wszystkie instancje tych danych, korzystając z funkcji rozkładania danych .
Na przykład aplikacja do blogowania społecznościowego może utworzyć post i jednocześnie zaktualizować go do kanału ostatniej aktywności i kanału aktywności użytkownika publikującego, używając kodu takiego jak ten:
Web modular API
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 namespaced API
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 zastosowano metodę push()
do utworzenia posta w węźle zawierającym posty dla wszystkich użytkowników w /posts/$postid
i jednoczesnego pobrania klucza. Klucz może być następnie użyty do utworzenia drugiego wpisu w postach użytkownika w /user-posts/$userid/$postid
.
Korzystając z tych ścieżek, możesz przeprowadzać jednoczesne aktualizacje w wielu lokalizacjach w drzewie JSON za pomocą jednego wywołania update()
, tak jak w tym przykładzie tworzony jest nowy wpis w obu lokalizacjach. Jednoczesne aktualizacje przeprowadzane w ten sposób są niepodzielne: albo wszystkie aktualizacje powiodą się, albo wszystkie aktualizacje się nie powiodą.
Dodaj wywołanie zwrotne zakończenia
Jeśli chcesz wiedzieć, kiedy Twoje dane zostały zatwierdzone, możesz dodać wywołanie zwrotne zakończenia. Zarówno set()
jak i update()
przyjmują opcjonalne wywołanie zwrotne zakończenia, które jest wywoływane, gdy zapis został zatwierdzony w bazie danych. Jeśli wywołanie nie powiodło się, wywołanie zwrotne przekazuje obiekt błędu wskazujący przyczynę niepowodzenia.
Web modular API
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 namespaced API
firebase.database().ref('users/' + userId).set({ username: name, email: email, profile_picture : imageUrl }, (error) => { if (error) { // The write failed... } else { // Data saved successfully! } });
Usunąć dane
Najprostszym sposobem usunięcia danych jest wywołanie metody remove()
w odniesieniu do lokalizacji tych danych.
Możesz także usunąć, podając null
jako wartość dla innej operacji zapisu, takiej jak set()
lub update()
. Tej techniki można użyć z update()
w celu usunięcia wielu elementów podrzędnych w jednym wywołaniu interfejsu API.
Otrzymaj Promise
Aby wiedzieć, kiedy Twoje dane zostaną przekazane na serwer Firebase Realtime Database, możesz użyć Promise
. Zarówno set()
jak i update()
mogą zwrócić Promise
możesz użyć, aby wiedzieć, kiedy zapis jest zatwierdzony w bazie danych.
Odłącz słuchaczy
Wywołania zwrotne są usuwane przez wywołanie metody off()
w odwołaniu do bazy danych Firebase.
Możesz usunąć pojedynczego słuchacza, przekazując go jako parametr do off()
. Wywołanie off()
w lokalizacji bez argumentów usuwa wszystkie detektory w tej lokalizacji.
Wywołanie funkcji off()
na odbiorniku nadrzędnym nie usuwa automatycznie detektorów zarejestrowanych w jego węzłach podrzędnych; off()
musi być również wywołana na wszystkich słuchaczach potomnych, aby usunąć wywołanie zwrotne.
Zapisz dane jako transakcje
Podczas pracy z danymi, które mogą zostać uszkodzone przez jednoczesne modyfikacje, takie jak liczniki przyrostowe, można użyć operacji transakcyjnej . Możesz nadać tej operacji funkcję aktualizacji i opcjonalne wywołanie zwrotne zakończenia. Funkcja aktualizacji przyjmuje jako argument bieżący stan danych i zwraca nowy żądany stan, który chcesz zapisać. Jeśli inny klient zapisze w lokalizacji przed pomyślnym zapisaniem nowej wartości, funkcja aktualizacji zostanie ponownie wywołana z nową bieżącą wartością i ponowiona zostanie próba zapisu.
Na przykład w przykładowej aplikacji do blogowania społecznościowego możesz zezwolić użytkownikom na oznaczanie postów gwiazdką i usuwaniem gwiazdek oraz śledzić, ile gwiazdek otrzymał post w następujący sposób:
Web modular API
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 namespaced API
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; }); }
Korzystanie z transakcji zapobiega nieprawidłowemu liczeniu gwiazdek, jeśli wielu użytkowników oznacza ten sam post w tym samym czasie lub klient ma nieaktualne dane. Jeśli transakcja zostanie odrzucona, serwer zwraca aktualną wartość klientowi, który ponownie uruchamia transakcję ze zaktualizowaną wartością. To się powtarza, dopóki transakcja nie zostanie zaakceptowana lub ją anulujesz.
Atomowe przyrosty po stronie serwera
W powyższym przykładzie użycia zapisujemy w bazie danych dwie wartości: identyfikator użytkownika, który oznaczył/usunął post gwiazdką, oraz zwiększoną liczbę gwiazdek. Jeśli wiemy już, że użytkownik oznacza post gwiazdką, zamiast transakcji możemy użyć atomowej operacji inkrementacji.
Web modular API
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 namespaced API
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 jest automatycznie uruchamiany ponownie, jeśli występuje konflikt aktualizacji. Ponieważ jednak operacja inkrementacji odbywa się bezpośrednio na serwerze bazy danych, nie ma możliwości wystąpienia konfliktu.
Jeśli chcesz wykryć i odrzucić konflikty specyficzne dla aplikacji, takie jak oznaczenie przez użytkownika posta, który już wcześniej oznaczył gwiazdką, należy napisać niestandardowe reguły bezpieczeństwa dla tego przypadku użycia.
Pracuj z danymi w trybie offline
Jeśli klient utraci połączenie sieciowe, Twoja aplikacja będzie nadal działać poprawnie.
Każdy klient podłączony do bazy danych Firebase utrzymuje własną wewnętrzną wersję wszelkich aktywnych danych. Gdy dane są zapisywane, są one najpierw zapisywane w tej wersji lokalnej. Następnie klient Firebase synchronizuje te dane ze zdalnymi serwerami baz danych i innymi klientami na zasadzie „najlepszych starań”.
W rezultacie wszystkie zapisy do bazy danych natychmiast wyzwalają zdarzenia lokalne, zanim jakiekolwiek dane zostaną zapisane na serwerze. Oznacza to, że Twoja aplikacja pozostaje responsywna niezależnie od opóźnienia sieci lub łączności.
Po przywróceniu łączności aplikacja otrzymuje odpowiedni zestaw zdarzeń, dzięki czemu klient synchronizuje się z bieżącym stanem serwera bez konieczności pisania niestandardowego kodu.
Więcej informacji na temat zachowania w trybie offline znajdziesz w sekcji Dowiedz się więcej o możliwościach w trybie online i offline .