Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Чтение и запись данных в Интернете

(Необязательно) Прототип и тестирование с помощью Firebase Local Emulator Suite

Прежде чем говорить о том, как ваше приложение считывает и записывает в базу данных в реальном времени, давайте представим набор инструментов, которые вы можете использовать для прототипирования и тестирования функциональности базы данных в реальном времени: Firebase Local Emulator Suite. Если вы пробуете разные модели данных, оптимизируете свои правила безопасности или работаете над поиском наиболее экономичного способа взаимодействия с серверной частью, возможность работать локально без развертывания живых сервисов может быть отличной идеей.

Эмулятор базы данных в реальном времени является частью Local Emulator Suite, который позволяет вашему приложению взаимодействовать с содержимым и конфигурацией эмулируемой базы данных, а также с дополнительными ресурсами вашего эмулируемого проекта (функциями, другими базами данных и правилами безопасности).

Использование эмулятора базы данных в реальном времени включает всего несколько шагов:

  1. Добавление строки кода в тестовую конфигурацию вашего приложения для подключения к эмулятору.
  2. Из корня вашей локальной директории проекта, работает firebase emulators:start .
  3. Выполнение вызовов из кода прототипа вашего приложения с помощью SDK платформы Realtime Database, как обычно, или с помощью Realtime Database REST API.

Подробное пошаговое руководство с участием в реальном времени базы данных и облачные функции доступны. Вы также должны взглянуть на введение Local Emulator сюита .

Получите ссылку на базу данных

Для чтения или записи данных из базы данных, вам нужен экземпляр firebase.database.Reference :

Веб-версия 9

import { getDatabase } from "firebase/database";

const database = getDatabase();

Веб-версия 8

var database = firebase.database();

Запись данных

В этом документе рассказывается об основах получения данных, а также о том, как упорядочивать и фильтровать данные Firebase.

Firebase данные извлекаются посредством прикрепления асинхронного слушателя к firebase.database.Reference . Слушатель запускается один раз для начального состояния данных и снова при каждом изменении данных.

Основные операции записи

Для базовых операций записи, вы можете использовать set() для сохранения данных в указанной ссылку, заменяя существующие данные на этом пути. Например, приложение для социального блоггинга может добавить пользователь с set() следующим образом :

Веб-версия 9

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
  });
}

Веб-версия 8

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

Используя set() перезаписывает данные в указанном месте, включая все дочерние узлы.

Прочитать данные

Прислушивайтесь к ценностным событиям

Для чтения данных на пути и прослушивать изменения, используйте on() или once() методу firebase.database.Reference наблюдать события.

Мероприятие Типичное использование
value Прочтите и прислушайтесь к изменениям всего содержимого пути.

Вы можете использовать value события , чтобы прочитать статический снимок содержимого на заданном пути, как они существовали в момент события. Этот метод запускается один раз при подключении слушателя и снова каждый раз при изменении данных, включая дочерние. Обратному вызову события передается моментальный снимок, содержащий все данные в этом месте, включая дочерние данные. Если нет данных, снимок будет возвращать false при вызове exists() и null при вызове val() на нем.

В следующем примере демонстрируется приложение для ведения социального блога, получающее количество звезд для публикации из базы данных:

Веб-версия 9

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);
});

Веб-версия 8

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() может увеличить использование пропускной способности и приводит к потере производительности, который может быть предотвращен с помощью слушателя в режиме реального времени , как показано выше.

Веб-версия 9

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);
});

Веб-версия 8

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() , чтобы получить данные из кэша локального диска сразу.

Это полезно для данных, которые необходимо загрузить только один раз, и не предполагается, что они будут часто меняться или требовать активного прослушивания. Например, приложение для ведения блога в предыдущих примерах использует этот метод для загрузки профиля пользователя, когда он начинает создание нового сообщения:

Веб-версия 9

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
});

Веб-версия 8

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() , вы можете обновить значения дочерних низкоуровневых, указав путь для ключа. Если данные хранятся в нескольких местах , чтобы лучше масштабировать, вы можете обновить все экземпляры этих данных , используя вентилятор-аут данных .

Например, приложение для ведения социального блога может создать сообщение и одновременно обновить его, добавив в него канал последних действий и канал активности пользователя, разместившего сообщение, используя такой код:

Веб-версия 9

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);
}

Веб-версия 8

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() принимает необязательный завершения обратного вызова, которая вызывается , когда запись была выполнена в базе данных. Если вызов был неудачным, обратному вызову передается объект ошибки, указывающий, почему произошел сбой.

Веб-версия 9

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...
});

Веб-версия 8

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 в реальном времени, вы можете использовать Promise . Оба set() и update() может возвращать Promise вы можете использовать , чтобы знать , когда записи фиксироваться в базе данных.

Отключить слушателей

Callbacks удаляются путем вызова off() метод на вашем Firebase ссылки базы данных.

Вы можете удалить один слушатель, передав его в качестве параметра для off() . Вызов off() на месте без аргументов удаляет все слушатели на этом месте.

Вызов off() на родительском слушателя не автоматически удалять слушателей , зарегистрированных на его дочерних узлов; off() также должен быть вызван на всех дочерних слушателей , чтобы удалить функцию обратного вызова.

Сохранять данные как транзакции

При работе с данными , которые могут быть испорчены одновременными изменениями, например, дополнительными счетчиками, вы можете использовать операцию транзакции . Вы можете дать этой операции функцию обновления и дополнительный обратный вызов завершения. Функция обновления принимает текущее состояние данных в качестве аргумента и возвращает новое желаемое состояние, которое вы хотите записать. Если другой клиент записывает в это место до того, как ваше новое значение будет успешно записано, ваша функция обновления вызывается снова с новым текущим значением, и запись повторяется.

Например, в примере приложения для ведения социального блога вы можете разрешить пользователям отмечать и снимать пометки с сообщений и отслеживать, сколько звезд получил сообщение, следующим образом:

Веб-версия 9

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;
  });
}

Веб-версия 8

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;
  });
}

Использование транзакции предотвращает неправильное подсчет звезд, если несколько пользователей отмечают один и тот же пост одновременно или у клиента были устаревшие данные. Если транзакция отклонена, сервер возвращает текущее значение клиенту, который снова запускает транзакцию с обновленным значением. Это повторяется до тех пор, пока транзакция не будет принята или вы не прервете транзакцию.

Атомарные приращения на стороне сервера

В приведенном выше варианте использования мы записываем в базу данных два значения: идентификатор пользователя, который помечает / снимает отметку с публикации, и увеличенное количество звезд. Если мы уже знаем, что пользователь отмечает публикацию, мы можем использовать операцию атомарного увеличения вместо транзакции.

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 синхронизирует эти данные с удаленными серверами баз данных и с другими клиентами по принципу «максимальных усилий».

В результате все записи в базу данных немедленно запускают локальные события, прежде чем какие-либо данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки в сети или подключения.

После восстановления подключения ваше приложение получает соответствующий набор событий, чтобы клиент синхронизировался с текущим состоянием сервера без необходимости писать какой-либо пользовательский код.

Мы больше о автономном поведении говорить Узнайте больше о онлайн и оффлайн возможности ..

Следующие шаги