(Необязательно) Создайте прототип и протестируйте его с помощью Firebase Local Emulator Suite
Прежде чем говорить о том, как ваше приложение читает и записывает в Realtime Database , давайте представим набор инструментов, которые вы можете использовать для прототипирования и тестирования функциональности Realtime Database : Firebase Local Emulator Suite . Если вы тестируете различные модели данных, оптимизируете свои правила безопасности или работаете над поиском наиболее экономичного способа взаимодействия с серверной частью, возможность работать локально без развертывания действующих сервисов может быть отличной идеей.
Эмулятор Realtime Database является частью Local Emulator Suite , который позволяет вашему приложению взаимодействовать с содержимым и конфигурацией эмулируемой базы данных, а также, при необходимости, с ресурсами эмулируемого проекта (функциями, другими базами данных и правилами безопасности).
Использование эмулятора Realtime Database включает всего несколько шагов:
- Добавление строки кода в тестовую конфигурацию вашего приложения для подключения к эмулятору.
- Из корня локального каталога проекта запустите
firebase emulators:start
. - Выполнение вызовов из кода прототипа вашего приложения с использованием SDK платформы Realtime Database , как обычно, или с помощью REST API Realtime Database .
Доступно подробное пошаговое руководство, включающее Realtime Database и Cloud Functions . Вам также следует ознакомиться с введением Local Emulator Suite .
Получить ссылку на базу данных
Чтобы читать или записывать данные из базы данных, вам понадобится экземпляр firebase.database.Reference
:
Web
import { getDatabase } from "firebase/database"; const database = getDatabase();
Web
var database = firebase.database();
Запись данных
В этом документе описаны основы получения данных, а также способы упорядочивания и фильтрации данных Firebase.
Данные Firebase извлекаются путем подключения асинхронного прослушивателя к firebase.database.Reference
. Прослушиватель запускается один раз для исходного состояния данных и снова при каждом изменении данных.
Основные операции записи
Для базовых операций записи вы можете использовать set()
для сохранения данных по указанной ссылке, заменяя любые существующие данные по этому пути. Например, приложение для ведения социальных блогов может добавить пользователя с помощью set()
следующим образом:
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 }); }
Использование set()
перезаписывает данные в указанном месте, включая любые дочерние узлы.
Чтение данных
Слушайте события значений
Чтобы читать данные по пути и прослушивать изменения, используйте onValue()
для наблюдения за событиями. Вы можете использовать это событие для чтения статических снимков содержимого по заданному пути в том виде, в котором оно существовало на момент события. Этот метод запускается один раз при подключении прослушивателя и снова каждый раз, когда изменяются данные, включая дочерние элементы. Обратному вызову события передается снимок, содержащий все данные в этом месте, включая дочерние данные. Если данных нет, снимок вернет false
когда вы вызываете exists()
и null
когда вы вызываете для него val()
.
В следующем примере показано, как приложение для ведения социальных блогов извлекает количество звезд поста из базы данных:
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); });
Прослушиватель получает snapshot
, содержащий данные в указанном месте базы данных на момент события. Вы можете получить данные из snapshot
с помощью метода val()
.
Считайте данные один раз
Прочитайте данные один раз с помощью get()
SDK предназначен для управления взаимодействием с серверами баз данных независимо от того, находится ли ваше приложение в сети или офлайн.
Как правило, вам следует использовать методы обработки событий, описанные выше, для чтения данных и получения уведомлений об обновлениях данных из серверной части. Методы прослушивания сокращают использование и выставление счетов и оптимизированы, чтобы предоставить вашим пользователям наилучшие впечатления при подключении к сети и офлайн.
Если вам нужны данные только один раз, вы можете использовать get()
чтобы получить снимок данных из базы данных. Если по какой-либо причине get()
не может вернуть значение сервера, клиент проверит кэш локального хранилища и вернет ошибку, если значение все еще не найдено.
Ненужное использование get()
может увеличить использование полосы пропускания и привести к потере производительности, которую можно предотвратить, используя прослушиватель реального времени, как показано выше.
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); });
Считайте данные один раз с наблюдателем
В некоторых случаях вам может потребоваться немедленное возвращение значения из локального кэша вместо проверки обновленного значения на сервере. В этих случаях вы можете использовать once()
для немедленного получения данных из кэша локального диска.
Это полезно для данных, которые необходимо загрузить только один раз и которые не будут часто меняться или требовать активного прослушивания. Например, приложение для ведения блога в предыдущих примерах использует этот метод для загрузки профиля пользователя, когда он начинает писать новую публикацию:
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'; // ... });
Обновление или удаление данных
Обновить определенные поля
Чтобы одновременно записывать данные в определенные дочерние узлы без перезаписи других дочерних узлов, используйте метод update()
.
При вызове update()
вы можете обновить дочерние значения нижнего уровня, указав путь к ключу. Если данные хранятся в нескольких местах для лучшего масштабирования, вы можете обновить все экземпляры этих данных, используя разветвление данных .
Например, приложение для ведения социальных блогов может создать публикацию и одновременно обновить ее до ленты недавних действий и ленты активности публикующего сообщения пользователя, используя такой код:
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); }
В этом примере push()
используется для создания сообщения в узле, содержащего сообщения для всех пользователей в /posts/$postid
и одновременного получения ключа. Затем ключ можно использовать для создания второй записи в сообщениях пользователя по адресу /user-posts/$userid/$postid
.
Используя эти пути, вы можете выполнять одновременные обновления в нескольких местах дерева JSON с помощью одного вызова update()
, например, как в этом примере создается новая запись в обоих местах. Одновременные обновления, выполняемые таким образом, являются атомарными: либо все обновления выполняются успешно, либо все обновления завершаются неудачно.
Добавьте обратный вызов завершения
Если вы хотите знать, когда ваши данные были зафиксированы, вы можете добавить обратный вызов завершения. И set()
, и update()
принимают необязательный обратный вызов завершения, который вызывается, когда запись зафиксирована в базе данных. Если вызов оказался неудачным, обратному вызову передается объект ошибки, указывающий, почему произошел сбой.
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! } });
Удалить данные
Самый простой способ удалить данные — вызвать метод remove()
по ссылке на расположение этих данных.
Вы также можете удалить, указав null
в качестве значения для другой операции записи, например set()
или update()
. Вы можете использовать этот метод с update()
для удаления нескольких дочерних элементов за один вызов API.
Получить Promise
Чтобы узнать, когда ваши данные будут переданы на сервер Firebase Realtime Database , вы можете использовать Promise
. И set()
, и update()
могут возвращать Promise
вы можете использовать, чтобы узнать, когда запись будет зафиксирована в базе данных.
Отключить прослушиватели
Обратные вызовы удаляются путем вызова метода off()
в ссылке на базу данных Firebase.
Вы можете удалить один прослушиватель, передав его в качестве параметра функции off()
. Вызов off()
для местоположения без аргументов удаляет всех прослушивателей в этом месте.
Вызов off()
родительского прослушивателя не удаляет автоматически прослушиватели, зарегистрированные на его дочерних узлах; off()
также должен быть вызван для всех дочерних прослушивателей, чтобы удалить обратный вызов.
Сохраняйте данные как транзакции
При работе с данными, которые могут быть повреждены в результате одновременных изменений, например с инкрементальными счетчиками, вы можете использовать операцию транзакции . Вы можете присвоить этой операции функцию обновления и дополнительный обратный вызов завершения. Функция обновления принимает текущее состояние данных в качестве аргумента и возвращает новое желаемое состояние, которое вы хотите записать. Если другой клиент записывает в это место до того, как ваше новое значение будет успешно записано, ваша функция обновления вызывается снова с новым текущим значением, и запись повторяется.
Например, в примере приложения для ведения социальных блогов вы можете разрешить пользователям отмечать и снимать пометки с публикаций, а также отслеживать, сколько звезд получило сообщение, следующим образом:
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; }); }
Использование транзакции предотвращает неправильное подсчет звезд, если несколько пользователей одновременно отмечают одну и ту же публикацию или у клиента есть устаревшие данные. Если транзакция отклонена, сервер возвращает текущее значение клиенту, который снова запускает транзакцию с обновленным значением. Это повторяется до тех пор, пока транзакция не будет принята или вы не прервете транзакцию.
Атомарные приращения на стороне сервера
В приведенном выше примере использования мы записываем в базу данных два значения: идентификатор пользователя, который помечает/снимает пометку с поста, и увеличенное количество звездочек. Если мы уже знаем, что пользователь отмечает публикацию пометкой, мы можем использовать операцию атомарного приращения вместо транзакции.
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); }
Этот код не использует операцию транзакции, поэтому он не перезапускается автоматически в случае конфликтующего обновления. Однако, поскольку операция приращения происходит непосредственно на сервере базы данных, вероятность конфликта отсутствует.
Если вы хотите обнаруживать и отклонять конфликты, специфичные для приложения, например, когда пользователь ставит пометку в публикации, которую он уже отмечал ранее, вам следует написать собственные правила безопасности для этого варианта использования.
Работайте с данными оффлайн
Если клиент потеряет сетевое соединение, ваше приложение продолжит работать правильно.
Каждый клиент, подключенный к базе данных Firebase, поддерживает свою собственную внутреннюю версию любых активных данных. Когда данные записываются, они сначала записываются в эту локальную версию. Затем клиент Firebase синхронизирует эти данные с удаленными серверами баз данных и с другими клиентами по принципу «максимально возможно».
В результате все записи в базу данных немедленно вызывают локальные события, прежде чем какие-либо данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки в сети или подключения.
После восстановления подключения ваше приложение получает соответствующий набор событий, чтобы клиент синхронизировался с текущим состоянием сервера без необходимости писать какой-либо собственный код.
Мы поговорим подробнее о поведении в автономном режиме в разделе Узнайте больше о возможностях онлайн и офлайн .
Следующие шаги
- Работа со списками данных
- Научитесь структурировать данные
- Узнайте больше об онлайн- и офлайн-возможностях
(Необязательно) Создайте прототип и протестируйте его с помощью Firebase Local Emulator Suite
Прежде чем говорить о том, как ваше приложение читает и записывает в Realtime Database , давайте представим набор инструментов, которые вы можете использовать для прототипирования и тестирования функциональности Realtime Database : Firebase Local Emulator Suite . Если вы тестируете различные модели данных, оптимизируете свои правила безопасности или работаете над поиском наиболее экономичного способа взаимодействия с серверной частью, возможность работать локально без развертывания действующих сервисов может быть отличной идеей.
Эмулятор Realtime Database является частью Local Emulator Suite , который позволяет вашему приложению взаимодействовать с содержимым и конфигурацией эмулируемой базы данных, а также, при необходимости, с ресурсами эмулируемого проекта (функциями, другими базами данных и правилами безопасности).
Использование эмулятора Realtime Database включает всего несколько шагов:
- Добавление строки кода в тестовую конфигурацию вашего приложения для подключения к эмулятору.
- Из корня локального каталога проекта запустите
firebase emulators:start
. - Выполнение вызовов из кода прототипа вашего приложения с использованием SDK платформы Realtime Database , как обычно, или с помощью REST API Realtime Database .
Доступно подробное пошаговое руководство, включающее Realtime Database и Cloud Functions . Вам также следует ознакомиться с введением Local Emulator Suite .
Получить ссылку на базу данных
Чтобы читать или записывать данные из базы данных, вам понадобится экземпляр firebase.database.Reference
:
Web
import { getDatabase } from "firebase/database"; const database = getDatabase();
Web
var database = firebase.database();
Запись данных
В этом документе описаны основы получения данных, а также способы упорядочивания и фильтрации данных Firebase.
Данные Firebase извлекаются путем подключения асинхронного прослушивателя к firebase.database.Reference
. Прослушиватель запускается один раз для исходного состояния данных и снова при каждом изменении данных.
Основные операции записи
Для базовых операций записи вы можете использовать set()
для сохранения данных по указанной ссылке, заменяя любые существующие данные по этому пути. Например, приложение для ведения социальных блогов может добавить пользователя с помощью set()
следующим образом:
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 }); }
Использование set()
перезаписывает данные в указанном месте, включая любые дочерние узлы.
Чтение данных
Слушайте события значений
Чтобы читать данные по пути и прослушивать изменения, используйте onValue()
для наблюдения за событиями. Вы можете использовать это событие для чтения статических снимков содержимого по заданному пути в том виде, в котором оно существовало на момент события. Этот метод запускается один раз при подключении прослушивателя и снова каждый раз, когда изменяются данные, включая дочерние элементы. Обратному вызову события передается снимок, содержащий все данные в этом месте, включая дочерние данные. Если данных нет, снимок вернет false
когда вы вызываете exists()
и null
когда вы вызываете для него val()
.
В следующем примере показано, как приложение для ведения социальных блогов извлекает количество звезд поста из базы данных:
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); });
Прослушиватель получает snapshot
, содержащий данные в указанном месте базы данных на момент события. Вы можете получить данные из snapshot
с помощью метода val()
.
Считайте данные один раз
Прочитайте данные один раз с помощью get()
SDK предназначен для управления взаимодействием с серверами баз данных независимо от того, находится ли ваше приложение в сети или офлайн.
Как правило, вам следует использовать методы обработки событий, описанные выше, для чтения данных и получения уведомлений об обновлениях данных из серверной части. Методы прослушивания сокращают использование и выставление счетов и оптимизированы, чтобы предоставить вашим пользователям наилучшие впечатления при подключении к сети и офлайн.
Если вам нужны данные только один раз, вы можете использовать get()
чтобы получить снимок данных из базы данных. Если по какой-либо причине get()
не может вернуть значение сервера, клиент проверит кэш локального хранилища и вернет ошибку, если значение все еще не найдено.
Ненужное использование get()
может увеличить использование полосы пропускания и привести к потере производительности, которую можно предотвратить, используя прослушиватель реального времени, как показано выше.
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); });
Считайте данные один раз с наблюдателем
В некоторых случаях вам может потребоваться немедленное возвращение значения из локального кэша вместо проверки обновленного значения на сервере. В этих случаях вы можете использовать once()
для немедленного получения данных из кэша локального диска.
Это полезно для данных, которые необходимо загрузить только один раз и которые не будут часто меняться или требовать активного прослушивания. Например, приложение для ведения блога в предыдущих примерах использует этот метод для загрузки профиля пользователя, когда он начинает писать новую публикацию:
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'; // ... });
Обновление или удаление данных
Обновить определенные поля
Чтобы одновременно записывать данные в определенные дочерние узлы без перезаписи других дочерних узлов, используйте метод update()
.
При вызове update()
вы можете обновить дочерние значения нижнего уровня, указав путь к ключу. Если данные хранятся в нескольких местах для лучшего масштабирования, вы можете обновить все экземпляры этих данных, используя разветвление данных .
Например, приложение для ведения социальных блогов может создать публикацию и одновременно обновить ее до ленты недавних действий и ленты активности публикующего сообщения пользователя, используя такой код:
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); }
В этом примере push()
используется для создания сообщения в узле, содержащего сообщения для всех пользователей в /posts/$postid
и одновременного получения ключа. Затем ключ можно использовать для создания второй записи в сообщениях пользователя по адресу /user-posts/$userid/$postid
.
Используя эти пути, вы можете выполнять одновременные обновления в нескольких местах дерева JSON с помощью одного вызова update()
, например, как в этом примере создается новая запись в обоих местах. Одновременные обновления, выполняемые таким образом, являются атомарными: либо все обновления выполняются успешно, либо все обновления завершаются неудачно.
Добавьте обратный вызов завершения
Если вы хотите знать, когда ваши данные были зафиксированы, вы можете добавить обратный вызов завершения. И set()
, и update()
принимают необязательный обратный вызов завершения, который вызывается, когда запись зафиксирована в базе данных. Если вызов оказался неудачным, обратному вызову передается объект ошибки, указывающий, почему произошел сбой.
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! } });
Удалить данные
Самый простой способ удалить данные — вызвать метод remove()
по ссылке на расположение этих данных.
Вы также можете удалить, указав null
в качестве значения для другой операции записи, например set()
или update()
. Вы можете использовать этот метод с update()
для удаления нескольких дочерних элементов за один вызов API.
Получить Promise
Чтобы узнать, когда ваши данные будут переданы на сервер Firebase Realtime Database , вы можете использовать Promise
. И set()
, и update()
могут возвращать Promise
вы можете использовать, чтобы узнать, когда запись будет зафиксирована в базе данных.
Отключить прослушиватели
Обратные вызовы удаляются путем вызова метода off()
в ссылке на базу данных Firebase.
Вы можете удалить один прослушиватель, передав его в качестве параметра функции off()
. Вызов off()
для местоположения без аргументов удаляет всех прослушивателей в этом месте.
Вызов off()
родительского прослушивателя не удаляет автоматически прослушиватели, зарегистрированные на его дочерних узлах; off()
также должен быть вызван для всех дочерних прослушивателей, чтобы удалить обратный вызов.
Сохраняйте данные как транзакции
При работе с данными, которые могут быть повреждены в результате одновременных изменений, например с инкрементальными счетчиками, вы можете использовать операцию транзакции . Вы можете присвоить этой операции функцию обновления и дополнительный обратный вызов завершения. Функция обновления принимает текущее состояние данных в качестве аргумента и возвращает новое желаемое состояние, которое вы хотите записать. Если другой клиент записывает в это место до того, как ваше новое значение будет успешно записано, ваша функция обновления вызывается снова с новым текущим значением, и запись повторяется.
Например, в примере приложения для ведения социальных блогов вы можете разрешить пользователям отмечать и снимать пометки с публикаций, а также отслеживать, сколько звезд получило сообщение, следующим образом:
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; }); }
Использование транзакции предотвращает неправильное подсчет звезд, если несколько пользователей одновременно отмечают одну и ту же публикацию или у клиента есть устаревшие данные. Если транзакция отклонена, сервер возвращает текущее значение клиенту, который снова запускает транзакцию с обновленным значением. Это повторяется до тех пор, пока транзакция не будет принята или вы не прервете транзакцию.
Атомарные приращения на стороне сервера
В приведенном выше примере использования мы записываем в базу данных два значения: идентификатор пользователя, который помечает или снимает пометку с поста, и увеличенное количество звездочек. Если мы уже знаем, что пользователь отмечает публикацию пометкой, мы можем использовать операцию атомарного приращения вместо транзакции.
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); }
Этот код не использует операцию транзакции, поэтому он не перезапускается автоматически в случае конфликтующего обновления. Однако, поскольку операция приращения происходит непосредственно на сервере базы данных, вероятность конфликта отсутствует.
Если вы хотите обнаруживать и отклонять конфликты, специфичные для приложения, например, когда пользователь ставит пометку в публикации, которую он уже отмечал ранее, вам следует написать собственные правила безопасности для этого варианта использования.
Работайте с данными оффлайн
Если клиент потеряет сетевое соединение, ваше приложение продолжит работать правильно.
Каждый клиент, подключенный к базе данных Firebase, поддерживает свою собственную внутреннюю версию любых активных данных. Когда данные записываются, они сначала записываются в эту локальную версию. Затем клиент Firebase синхронизирует эти данные с удаленными серверами баз данных и с другими клиентами по принципу «максимально возможно».
В результате все записи в базу данных немедленно вызывают локальные события, прежде чем какие-либо данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки в сети или подключения.
После восстановления подключения ваше приложение получает соответствующий набор событий, чтобы клиент синхронизировался с текущим состоянием сервера без необходимости писать какой-либо специальный код.
Мы поговорим подробнее о поведении в автономном режиме в разделе Узнайте больше о возможностях онлайн и офлайн .
Следующие шаги
- Работа со списками данных
- Научитесь структурировать данные
- Узнайте больше об онлайн- и офлайн-возможностях
(Необязательно) Создайте прототип и протестируйте его с помощью Firebase Local Emulator Suite
Прежде чем говорить о том, как ваше приложение читает и записывает в Realtime Database , давайте представим набор инструментов, которые вы можете использовать для прототипирования и тестирования функциональности Realtime Database : Firebase Local Emulator Suite . Если вы тестируете различные модели данных, оптимизируете правила безопасности или работаете над поиском наиболее экономичного способа взаимодействия с серверной частью, возможность работать локально без развертывания действующих сервисов может быть отличной идеей.
Эмулятор Realtime Database является частью Local Emulator Suite , который позволяет вашему приложению взаимодействовать с содержимым и конфигурацией эмулируемой базы данных, а также, при необходимости, с ресурсами эмулируемого проекта (функциями, другими базами данных и правилами безопасности).
Использование эмулятора Realtime Database включает всего несколько шагов:
- Добавление строки кода в тестовую конфигурацию вашего приложения для подключения к эмулятору.
- Из корня локального каталога проекта запустите
firebase emulators:start
. - Выполнение вызовов из кода прототипа вашего приложения с использованием SDK платформы Realtime Database , как обычно, или с помощью REST API Realtime Database .
Доступно подробное пошаговое руководство, включающее Realtime Database и Cloud Functions . Вам также следует ознакомиться с введением Local Emulator Suite .
Получить ссылку на базу данных
Чтобы читать или записывать данные из базы данных, вам понадобится экземпляр firebase.database.Reference
:
Web
import { getDatabase } from "firebase/database"; const database = getDatabase();
Web
var database = firebase.database();
Запись данных
В этом документе описаны основы получения данных, а также способы упорядочивания и фильтрации данных Firebase.
Данные Firebase извлекаются путем подключения асинхронного прослушивателя к firebase.database.Reference
. Прослушиватель запускается один раз для исходного состояния данных и снова при каждом изменении данных.
Основные операции записи
Для базовых операций записи вы можете использовать set()
для сохранения данных по указанной ссылке, заменяя любые существующие данные по этому пути. Например, приложение для ведения социальных блогов может добавить пользователя с помощью set()
следующим образом:
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 }); }
Использование set()
перезаписывает данные в указанном месте, включая любые дочерние узлы.
Чтение данных
Слушайте события значений
Чтобы читать данные по пути и прослушивать изменения, используйте onValue()
для наблюдения за событиями. Вы можете использовать это событие для чтения статических снимков содержимого по заданному пути в том виде, в котором оно существовало на момент события. Этот метод запускается один раз при подключении прослушивателя и снова каждый раз, когда изменяются данные, включая дочерние элементы. Обратному вызову события передается снимок, содержащий все данные в этом месте, включая дочерние данные. Если данных нет, снимок вернет false
когда вы вызываете exists()
и null
когда вы вызываете для него val()
.
В следующем примере показано, как приложение для ведения социальных блогов извлекает количество звезд поста из базы данных:
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); });
Прослушиватель получает snapshot
, содержащий данные в указанном месте базы данных на момент события. Вы можете получить данные из snapshot
с помощью метода val()
.
Считайте данные один раз
Прочитайте данные один раз с помощью get()
SDK предназначен для управления взаимодействием с серверами баз данных независимо от того, находится ли ваше приложение в сети или офлайн.
Как правило, вам следует использовать методы обработки событий, описанные выше, для чтения данных и получения уведомлений об обновлениях данных из серверной части. Методы прослушивания сокращают использование и выставление счетов и оптимизированы, чтобы предоставить вашим пользователям наилучшие впечатления при подключении к сети и офлайн.
Если вам нужны данные только один раз, вы можете использовать get()
чтобы получить снимок данных из базы данных. Если по какой-либо причине get()
не может вернуть значение сервера, клиент проверит кэш локального хранилища и вернет ошибку, если значение все еще не найдено.
Ненужное использование get()
может увеличить использование полосы пропускания и привести к потере производительности, которую можно предотвратить, используя прослушиватель реального времени, как показано выше.
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); });
Считайте данные один раз с наблюдателем
В некоторых случаях вам может потребоваться немедленное возвращение значения из локального кэша вместо проверки обновленного значения на сервере. В этих случаях вы можете использовать once()
для немедленного получения данных из кэша локального диска.
Это полезно для данных, которые необходимо загрузить только один раз и которые не будут часто меняться или требовать активного прослушивания. Например, приложение для ведения блога в предыдущих примерах использует этот метод для загрузки профиля пользователя, когда он начинает писать новую публикацию:
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'; // ... });
Обновление или удаление данных
Обновить определенные поля
Чтобы одновременно записывать данные в определенные дочерние узлы без перезаписи других дочерних узлов, используйте метод update()
.
При вызове update()
вы можете обновить дочерние значения нижнего уровня, указав путь к ключу. Если данные хранятся в нескольких местах для лучшего масштабирования, вы можете обновить все экземпляры этих данных, используя разветвление данных .
Например, приложение для ведения социальных блогов может создать публикацию и одновременно обновить ее до ленты недавних действий и ленты активности публикующего сообщения пользователя, используя такой код:
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); }
В этом примере push()
используется для создания сообщения в узле, содержащего сообщения для всех пользователей в /posts/$postid
и одновременного получения ключа. Затем ключ можно использовать для создания второй записи в сообщениях пользователя по адресу /user-posts/$userid/$postid
.
Используя эти пути, вы можете выполнять одновременные обновления в нескольких местах в дереве JSON с помощью одного вызова update()
, например, как в этом примере создается новая запись в обоих местах. Одновременные обновления, выполняемые таким образом, являются атомарными: либо все обновления выполняются успешно, либо все обновления завершаются неудачей.
Добавьте обратный вызов завершения
Если вы хотите знать, когда ваши данные были зафиксированы, вы можете добавить обратный вызов завершения. И set()
, и update()
принимают необязательный обратный вызов завершения, который вызывается, когда запись зафиксирована в базе данных. Если вызов оказался неудачным, обратному вызову передается объект ошибки, указывающий, почему произошел сбой.
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! } });
Удалить данные
Самый простой способ удалить данные — вызвать метод remove()
по ссылке на расположение этих данных.
Вы также можете удалить, указав null
в качестве значения для другой операции записи, например set()
или update()
. Вы можете использовать этот метод с update()
для удаления нескольких дочерних элементов за один вызов API.
Получить Promise
Чтобы узнать, когда ваши данные будут переданы на сервер Firebase Realtime Database , вы можете использовать Promise
. И set()
, и update()
могут возвращать Promise
вы можете использовать, чтобы узнать, когда запись будет зафиксирована в базе данных.
Отключить прослушиватели
Обратные вызовы удаляются путем вызова метода off()
в ссылке на базу данных Firebase.
Вы можете удалить один прослушиватель, передав его в качестве параметра функции off()
. Вызов off()
для местоположения без аргументов удаляет всех прослушивателей в этом месте.
Вызов off()
родительского прослушивателя не удаляет автоматически прослушиватели, зарегистрированные на его дочерних узлах; off()
также должен быть вызван для всех дочерних прослушивателей, чтобы удалить обратный вызов.
Сохраняйте данные как транзакции
При работе с данными, которые могут быть повреждены в результате одновременных изменений, например с инкрементальными счетчиками, вы можете использовать операцию транзакции . Вы можете дать этой операции функцию обновления и необязательный обратный вызов завершения. Функция обновления принимает текущее состояние данных в качестве аргумента и возвращает новое желаемое состояние, которое вы хотели бы написать. Если другой клиент пишет в местоположение до того, как ваше новое значение будет успешно записано, функция вашего обновления снова вызывается с новым текущим значением, и запись повторно подходит.
Например, в примере приложения социального блогов вы можете позволить пользователям сниматься и неустановить посты и отслеживать, сколько звезд получило сообщение следующим образом:
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; }); }
Использование транзакции предотвращает неверное количество звезд, если несколько пользователей становятся одновременно одновременно один пост, или у клиента были устаревшие данные. Если транзакция отклоняется, сервер возвращает текущее значение клиенту, которое снова запускает транзакцию с обновленным значением. Это повторяется до тех пор, пока транзакция не будет принята, или вы прервите транзакцию.
Атомный сервер-шаг на стороне
В приведенном выше случае использования мы пишем два значения в базу данных: идентификатор пользователя, который играет/вызывает пост, и увеличенное количество звезд. Если мы уже знаем, что пользователь играет главную роль в сообщении, мы можем использовать операцию по атомному приращению вместо транзакции.
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); }
Этот код не использует операцию транзакции, поэтому он не автоматически запускается, если существует противоречивое обновление. Однако, поскольку операция приращения происходит непосредственно на сервере базы данных, нет шансов на конфликт.
Если вы хотите обнаружить и отклонить конфликты, конкретные для приложения, такие как пользователь с участием поста, который он уже снимался ранее, вам следует написать пользовательские правила безопасности для этого варианта использования.
Работать с данными в автономном режиме
Если клиент теряет свое сетевое соединение, ваше приложение будет продолжать правильно функционировать.
Каждый клиент, подключенный к базе данных Firebase, поддерживает собственную внутреннюю версию любых активных данных. Когда данные написаны, сначала написаны в эту локальную версию. Затем клиент Firebase синхронизирует эти данные с удаленными серверами базы данных и с другими клиентами на основе «наиболее эффективных».
В результате все записываются в базу данных, запускающую локальные события, немедленно, прежде чем какие -либо данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки сети или подключения.
После восстановления подключения, ваше приложение получает соответствующий набор событий, чтобы клиент синхронизировал с текущим состоянием сервера, не записав какой -либо пользовательский код.
Мы поговорим больше о автономном поведении в узнать больше о онлайн -и автономных возможностях ..
Следующие шаги
- Работа со списками данных
- Узнайте, как структурировать данные
- Узнайте больше о онлайн -и автономных возможностях
(Необязательно) Прототип и тестирование с помощью Firebase Local Emulator Suite
Прежде чем говорить о том, как ваше приложение читает и записывается в Realtime Database , давайте представим набор инструментов, которые вы можете использовать для прототипа и проверки функций Realtime Database : Firebase Local Emulator Suite . Если вы пробуете различные модели данных, оптимизируете свои правила безопасности или работаете над тем, чтобы найти наиболее экономически эффективный способ взаимодействия с бэк-эндом, возможность работать на местном уровне без развертывания живых услуг может быть отличной идеей.
Эмулятор Realtime Database является частью Local Emulator Suite , который позволяет вашему приложению взаимодействовать с вашим эмулированным содержанием базы данных и конфигурацией, а также, опционально ваши эмулированные ресурсы проекта (функции, другие базы данных и правила безопасности).
Использование эмулятора Realtime Database включает в себя всего несколько шагов:
- Добавление строки кода в тестовую конфигурацию вашего приложения для подключения к эмулятору.
- Из корня вашего местного каталога проекта управляет
firebase emulators:start
. - Создание вызовов из кода прототипа вашего приложения с использованием платформы Realtime Database , как обычно, или с использованием API REST Realtime Database .
Доступно подробное прохождение с участием Realtime Database и Cloud Functions . Вы также должны взглянуть на введение Local Emulator Suite .
Получите ссылку на базу данных
Чтобы прочитать или записать данные из базы данных, вам нужен экземпляр firebase.database.Reference
:
Web
import { getDatabase } from "firebase/database"; const database = getDatabase();
Web
var database = firebase.database();
Напишите данные
Этот документ охватывает основы извлечения данных и того, как заказать и фильтровать данные Firebase.
Данные Firebase извлекаются путем прикрепления асинхронного слушателя к firebase.database.Reference
. Слушатель запускается один раз для начального состояния данных и снова в любое время, когда данные меняются.
Основные операции записи
Для основных операций записи вы можете использовать set()
для сохранения данных в указанном эталонном эталоне, заменив любые существующие данные на этом пути. Например, приложение социального блога может добавить пользователя с set()
следующим образом:
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 }); }
Использование set()
перезаписывает данные в указанном месте, включая любые узлы дочерних.
Прочитать данные
Слушайте события ценности
Чтобы прочитать данные на пути и прослушать изменения, используйте onValue()
для наблюдения за событиями. Вы можете использовать это событие для чтения статических снимков содержимого на данном пути, поскольку они существовали во время события. Этот метод запускается один раз, когда слушатель прикреплен и снова каждый раз, когда данные, включая детей, изменяются. Обратный вызов события проходит снимок, содержащий все данные в этом месте, включая данные о дочерних данных. Если нет данных, снимки вернется false
когда вы вызовуте exists()
и null
, когда вы вызовуте val()
на нем.
В следующем примере демонстрируется приложение для социального ведения блога, получающее количество звезд из поста из базы данных:
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); });
Слушатель получает snapshot
, который содержит данные в указанном месте в базе данных во время события. Вы можете получить данные в snapshot
с помощью метода val()
.
Прочитайте данные один раз
Прочитайте данные один раз с помощью get ()
SDK предназначен для управления взаимодействиями с серверами базы данных, независимо от того, является ли ваше приложение онлайн или офлайн.
Как правило, вы должны использовать методы события значения, описанные выше, чтобы прочитать данные, чтобы получить уведомление об обновлениях данных из бэкэнда. Техника слушателя уменьшает ваше использование и выставление счетов, и они оптимизированы, чтобы дать вашим пользователям лучший опыт, когда они выходят в Интернет и офлайн.
Если вам нужны данные только один раз, вы можете использовать get()
чтобы получить снимок данных из базы данных. Если по какой -либо причине get()
не может вернуть значение сервера, клиент будет исследовать локальный кэш хранения и вернуть ошибку, если значение все еще не найдено.
Ненужное использование get()
может увеличить использование полосы пропускания и привести к потере производительности, что можно предотвратить, используя слушателя в реальном времени, как показано выше.
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); });
Прочитайте данные один раз с наблюдателем
В некоторых случаях вы можете захотеть немедленно возвращать значение из локального кэша, вместо того, чтобы проверить обновленное значение на сервере. В этих случаях вы можете использовать once()
чтобы немедленно получить данные из локального кеша диска.
Это полезно для данных, которые необходимо загрузить только один раз и не ожидается, что они будут часто меняться или требовать активного прослушивания. Например, приложение для блогов в предыдущих примерах использует этот метод для загрузки профиля пользователя, когда они начнут создавать новое сообщение:
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'; // ... });
Обновление или удаление данных
Обновите конкретные поля
Чтобы одновременно написать конкретным детям узла, не перезаписывая другие дочерние узлы, используйте метод update()
.
При вызове update()
вы можете обновить дочерние значения нижнего уровня, указав путь для ключа. Если данные хранятся в нескольких местах, чтобы лучше масштабировать, вы можете обновить все экземпляры этих данных, используя разветвление данных .
Например, приложение для социального блога может создать сообщение и одновременно обновить его до недавнего канала деятельности и подачи подачи пользователя, используя код, подобный этим:
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); }
В этом примере используется push()
для создания поста в узле, содержащем сообщения для всех пользователей по адресу /posts/$postid
и одновременно получает ключ. Затем ключ можно использовать для создания второй записи в сообщениях пользователя AT /user-posts/$userid/$postid
.
Используя эти пути, вы можете выполнять одновременные обновления в несколько мест в дереве JSON с одним вызовом для update()
, например, как этот пример создает новый пост в обоих местах. Одновременные обновления, сделанные таким образом, являются атомными: либо все обновления достигают успеха, либо все обновления не сняты.
Добавить обратный вызов
Если вы хотите знать, когда ваши данные будут совершены, вы можете добавить обратный вызов завершения. Оба set()
и update()
принимают необязательный обратный вызов завершения, который вызывается, когда запись была привержена базе данных. Если вызов был неудачным, обратный вызов передается объектом ошибки, указывающим, почему произошел сбой.
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! } });
Удалить данные
Самый простой способ удаления данных - вызовать remove()
на ссылке на местоположение этих данных.
Вы также можете удалить, указав null
в качестве значения для другой операции записи, такой как set()
или update()
. Вы можете использовать эту технику с помощью update()
для удаления нескольких детей в одном вызове API.
Получить Promise
Чтобы узнать, когда ваши данные будут представлены на сервере Firebase Realtime Database , вы можете использовать Promise
. Оба set()
и update()
могут вернуть Promise
которое вы можете использовать, чтобы узнать, когда запись привержена базе данных.
Снимите слушателей
Обратные вызовы удаляются, вызывая метод off()
в вашей ссылке на базу данных FireBase.
Вы можете удалить одного слушателя, передав его в виде параметра с off()
. off()
в месте без аргументов удаляет всех слушателей в этом месте.
off()
на родительском слушателе не автоматически удаляет слушателей, зарегистрированных на своих дочерних узлах; off()
также должен быть вызван любым слушателям, чтобы удалить обратный вызов.
Сохранить данные как транзакции
При работе с данными, которые могут быть повреждены одновременными модификациями, такими как инкрементные счетчики, вы можете использовать операцию транзакции . Вы можете дать этой операции функцию обновления и необязательный обратный вызов завершения. Функция обновления принимает текущее состояние данных в качестве аргумента и возвращает новое желаемое состояние, которое вы хотели бы написать. Если другой клиент пишет в местоположение до того, как ваше новое значение будет успешно записано, функция вашего обновления снова вызывается с новым текущим значением, и запись повторно подходит.
Например, в примере приложения социального блогов вы можете позволить пользователям сниматься и неустановить посты и отслеживать, сколько звезд получило сообщение следующим образом:
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; }); }
Использование транзакции предотвращает неверное количество звезд, если несколько пользователей становятся одновременно одновременно один пост, или у клиента были устаревшие данные. Если транзакция отклоняется, сервер возвращает текущее значение клиенту, которое снова запускает транзакцию с обновленным значением. Это повторяется до тех пор, пока транзакция не будет принята, или вы прервите транзакцию.
Атомный сервер-шаг на стороне
В приведенном выше случае использования мы пишем два значения в базу данных: идентификатор пользователя, который играет/вызывает пост, и увеличенное количество звезд. Если мы уже знаем, что пользователь играет главную роль в сообщении, мы можем использовать операцию по атомному приращению вместо транзакции.
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); }
Этот код не использует операцию транзакции, поэтому он не автоматически запускается, если существует противоречивое обновление. Однако, поскольку операция приращения происходит непосредственно на сервере базы данных, нет шансов на конфликт.
Если вы хотите обнаружить и отклонить конфликты, конкретные для приложения, такие как пользователь с участием поста, который он уже снимался ранее, вам следует написать пользовательские правила безопасности для этого варианта использования.
Работать с данными в автономном режиме
Если клиент теряет свое сетевое соединение, ваше приложение будет продолжать правильно функционировать.
Каждый клиент, подключенный к базе данных Firebase, поддерживает собственную внутреннюю версию любых активных данных. Когда данные написаны, сначала написаны в эту локальную версию. Затем клиент Firebase синхронизирует эти данные с удаленными серверами базы данных и с другими клиентами на основе «наиболее эффективных».
В результате все записываются в базу данных, запускающую локальные события, немедленно, прежде чем какие -либо данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки сети или подключения.
После восстановления подключения, ваше приложение получает соответствующий набор событий, чтобы клиент синхронизировал с текущим состоянием сервера, не записав какой -либо пользовательский код.
Мы поговорим больше о автономном поведении в узнать больше о онлайн -и автономных возможностях ..