קרא וכתוב נתונים באינטרנט

(אופציונלי) אב-טיפוס ובדיקה עם Firebase Local Emulator Suite

לפני שנדבר על האופן שבו האפליקציה שלך קוראת וכותבת למסד נתונים בזמן אמת, הבה נציג קבוצה של כלים שבהם אתה יכול להשתמש כדי לבחון את הפונקציונליות של מסד נתונים בזמן אמת: Firebase Local Emulator Suite. אם אתה מנסה מודלים שונים של נתונים, מייעל את כללי האבטחה שלך, או עובד כדי למצוא את הדרך המשתלמת ביותר לאינטראקציה עם הקצה האחורי, היכולת לעבוד מקומית מבלי לפרוס שירותים חיים יכולה להיות רעיון מצוין.

אמולטור של מסד נתונים בזמן אמת הוא חלק מחבילת האימולטור המקומית, המאפשרת לאפליקציה שלך לקיים אינטראקציה עם תוכן מסד הנתונים ותצורת הדמיית שלך, כמו גם עם משאבי הפרויקט המחויכים שלך (פונקציות, מסדי נתונים אחרים וכללי אבטחה).

השימוש באמולטור Realtime Database כולל רק כמה שלבים:

  1. הוספת שורת קוד לתצורת הבדיקה של האפליקציה שלך כדי להתחבר לאמולטור.
  2. מהשורש של ספריית הפרויקט המקומית שלך, הפעל firebase emulators:start .
  3. ביצוע שיחות מקוד אב הטיפוס של האפליקציה שלך באמצעות SDK של פלטפורמת Realtime Database כרגיל, או באמצעות Realtime Database REST API.

הדרכה מפורטת הכוללת מסד נתונים ופונקציות ענן בזמן אמת זמין. כדאי גם לעיין במבוא של Local Emulator Suite .

קבל הפניה למסד נתונים

כדי לקרוא או לכתוב נתונים ממסד הנתונים, אתה צריך מופע של firebase.database.Reference :

Web modular API

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web namespaced API

var database = firebase.database();

כתוב נתונים

מסמך זה מכסה את היסודות של אחזור נתונים וכיצד להזמין ולסנן נתוני Firebase.

נתוני Firebase מאוחזרים על ידי הצמדת מאזין אסינכרוני ל- firebase.database.Reference . המאזין מופעל פעם אחת עבור המצב ההתחלתי של הנתונים ושוב בכל פעם שהנתונים משתנים.

פעולות כתיבה בסיסיות

עבור פעולות כתיבה בסיסיות, אתה יכול להשתמש set() כדי לשמור נתונים בהפניה שצוינה, תוך החלפת כל הנתונים הקיימים בנתיב זה. לדוגמה, אפליקציית בלוגים חברתית עשויה להוסיף משתמש עם set() באופן הבא:

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

שימוש set() מחליף נתונים במיקום שצוין, כולל כל צמתים צאצאים.

קרא נתונים

הקשיבו לאירועים ערכיים

כדי לקרוא נתונים בנתיב ולהקשיב לשינויים, השתמש onValue() כדי לצפות באירועים. אתה יכול להשתמש באירוע זה כדי לקרוא תמונות סטטיות של התוכן בנתיב נתון, כפי שהם היו קיימים בזמן האירוע. שיטה זו מופעלת פעם אחת כאשר המאזין מחובר ושוב בכל פעם שהנתונים, כולל ילדים, משתנים. ההתקשרות חזרה לאירוע מועברת תמונת מצב המכילה את כל הנתונים באותו מיקום, כולל נתוני ילדים. אם אין נתונים, תמונת המצב תחזיר false כאשר אתה קורא exists() ו- null כאשר אתה קורא val() עליו.

הדוגמה הבאה מדגימה אפליקציית בלוגים חברתית המאחזרת את ספירת הכוכבים של פוסט ממסד הנתונים:

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

המאזין מקבל snapshot המכילה את הנתונים במיקום שצוין במסד הנתונים בזמן האירוע. אתה יכול לאחזר את הנתונים snapshot עם השיטה val() .

קרא נתונים פעם אחת

קרא נתונים פעם אחת עם get()

ה-SDK נועד לנהל אינטראקציות עם שרתי מסד נתונים בין אם האפליקציה שלך מקוונת או לא מקוונת.

באופן כללי, עליך להשתמש בטכניקות אירועי ערך המתוארות לעיל כדי לקרוא נתונים כדי לקבל הודעה על עדכונים לנתונים מה-backend. טכניקות ההאזנה מפחיתות את השימוש והחיוב שלך, והן מותאמות כדי להעניק למשתמשים שלך את החוויה הטובה ביותר בזמן שהם נכנסים לאינטרנט ולא מקוון.

אם אתה צריך את הנתונים פעם אחת בלבד, אתה יכול להשתמש get() כדי לקבל תמונת מצב של הנתונים ממסד הנתונים. אם מסיבה כלשהי get() אינו מסוגל להחזיר את ערך השרת, הלקוח יבדוק את מטמון האחסון המקומי ויחזיר שגיאה אם ​​הערך עדיין לא נמצא.

שימוש מיותר ב- get() יכול להגביר את השימוש ברוחב הפס ולהוביל לאובדן ביצועים, שניתן למנוע על ידי שימוש במאזין בזמן אמת כפי שמוצג לעיל.

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

קרא נתונים פעם אחת עם צופה

במקרים מסוימים ייתכן שתרצה שהערך מהמטמון המקומי יוחזר באופן מיידי, במקום לבדוק אם יש ערך מעודכן בשרת. במקרים אלה אתה יכול להשתמש once() כדי לקבל את הנתונים ממטמון הדיסק המקומי באופן מיידי.

זה שימושי עבור נתונים שצריך לטעון רק פעם אחת ואינם צפויים להשתנות לעתים קרובות או לדרוש האזנה פעילה. לדוגמה, אפליקציית הבלוגים בדוגמאות הקודמות משתמשת בשיטה זו כדי לטעון פרופיל של משתמש כאשר הוא מתחיל לכתוב פוסט חדש:

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

עדכון או מחיקת נתונים

עדכן שדות ספציפיים

כדי לכתוב בו-זמנית לילדים ספציפיים של צומת מבלי לדרוס צמתים צאצאים אחרים, השתמש בשיטת update() .

בעת קריאה update() , תוכל לעדכן ערכי צאצא ברמה נמוכה יותר על ידי ציון נתיב עבור המפתח. אם הנתונים מאוחסנים במספר מיקומים כדי להרחיב את גודלם טוב יותר, אתה יכול לעדכן את כל המופעים של נתונים אלה באמצעות מניפת נתונים .

לדוגמה, אפליקציית בלוגים חברתית עשויה ליצור פוסט ולעדכן אותו בו-זמנית לעדכון הפעילות האחרונה ולפיד הפעילות של המשתמש המפרסם באמצעות קוד כזה:

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

דוגמה זו משתמשת push() כדי ליצור פוסט בצומת המכיל פוסטים עבור כל המשתמשים ב- /posts/$postid ובו-זמנית לאחזר את המפתח. לאחר מכן ניתן להשתמש במפתח ליצירת ערך שני בפוסטים של המשתמש ב- /user-posts/$userid/$postid .

באמצעות נתיבים אלה, אתה יכול לבצע עדכונים בו-זמנית למספר מיקומים בעץ ה-JSON עם קריאה יחידה ל- update() , כגון האופן שבו דוגמה זו יוצרת את הפוסט החדש בשני המיקומים. עדכונים בו-זמניים שנעשו בדרך זו הם אטומיים: או שכל העדכונים מצליחים או שכל העדכונים נכשלים.

הוסף התקשרות חוזרת של השלמה

אם אתה רוצה לדעת מתי הנתונים שלך בוצעו, אתה יכול להוסיף התקשרות חזרה. גם set() וגם update() מקבלים התקשרות אופציונלית להשלמה שנקראת כאשר הכתיבה הוגדרה למסד הנתונים. אם השיחה לא הצליחה, ההתקשרות חזרה מועברת אובייקט שגיאה המציין מדוע התרחש הכשל.

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

מחק נתונים

הדרך הפשוטה ביותר למחוק נתונים היא לקרוא ל- remove() בהפניה למיקום הנתונים הללו.

אתה יכול גם למחוק על ידי ציון null כערך עבור פעולת כתיבה אחרת כגון set() או update() . אתה יכול להשתמש בטכניקה זו עם update() כדי למחוק ילדים מרובים בקריאה אחת ל-API.

קבל Promise

כדי לדעת מתי הנתונים שלך מחויבים לשרת Firebase Realtime Database, אתה יכול להשתמש Promise . גם set() וגם update() יכולים להחזיר Promise שבה תוכלו להשתמש כדי לדעת מתי הכתיבה מחויבת למסד הנתונים.

לנתק מאזינים

התקשרויות חוזרות מוסרות על ידי קריאה לשיטה off() בהפניה למסד הנתונים שלך ב-Firebase.

אתה יכול להסיר מאזין בודד על ידי העברתו כפרמטר ל- off() . קריאת off() במיקום ללא ארגומנטים מסירה את כל המאזינים במיקום זה.

קריאת off() במאזין אב אינה מסירה אוטומטית מאזינים הרשומים בצמתי הצאצא שלו; off() חייב להיקרא גם על כל מאזיני ילדים כדי להסיר את ההתקשרות חזרה.

שמור נתונים כעסקאות

כשאתה עובד עם נתונים שעלולים להיפגם על ידי שינויים בו-זמניים, כגון מונים מצטברים, אתה יכול להשתמש בפעולת עסקה . אתה יכול לתת לפעולה זו פונקציית עדכון והתקשרות אופציונלית להשלמה. פונקציית העדכון לוקחת את המצב הנוכחי של הנתונים כארגומנט ומחזירה את המצב הרצוי החדש שתרצה לכתוב. אם לקוח אחר כותב למיקום לפני שהערך החדש שלך נכתב בהצלחה, פונקציית העדכון שלך נקראת שוב עם הערך הנוכחי החדש, והכתיבה מתבצעת מחדש.

לדוגמה, באפליקציית הבלוגים החברתיים לדוגמה, תוכל לאפשר למשתמשים לסמן ולבטל כוכב בפוסטים ולעקוב אחר כמה כוכבים פוסט קיבל באופן הבא:

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

השימוש בעסקה מונע מספירת כוכבים להיות שגויה אם מספר משתמשים מככבים באותו פוסט בו-זמנית או שללקוח היו נתונים מיושנים. אם העסקה נדחתה, השרת מחזיר את הערך הנוכחי ללקוח, אשר מריץ את העסקה שוב עם הערך המעודכן. זה חוזר על עצמו עד שהעסקה תתקבל או שתבטל את העסקה.

מרווחים בצד השרת האטומי

במקרה השימוש שלמעלה אנו כותבים שני ערכים למסד הנתונים: מזהה המשתמש שמככב/מבטל את הכוכב בפוסט, וספירת הכוכבים המוגדלת. אם אנחנו כבר יודעים שהמשתמש מסמן את הפוסט בכוכב, נוכל להשתמש בפעולת תוספת אטומית במקום בעסקה.

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

קוד זה אינו משתמש בפעולת עסקה, כך שהוא אינו מופעל מחדש באופן אוטומטי אם יש עדכון מתנגש. עם זאת, מכיוון שפעולת ההגדלה מתרחשת ישירות על שרת מסד הנתונים, אין סיכוי להתנגשות.

אם ברצונך לזהות ולדחות התנגשויות ספציפיות ליישום, כגון משתמש המסמן פוסט שהוא כבר סימן בכוכב לפני כן, עליך לכתוב כללי אבטחה מותאמים אישית עבור מקרה השימוש הזה.

עבודה עם נתונים במצב לא מקוון

אם לקוח מאבד את חיבור הרשת שלו, האפליקציה שלך תמשיך לפעול כהלכה.

כל לקוח המחובר למסד נתונים של Firebase שומר על גרסה פנימית משלו של כל נתונים פעילים. כאשר נתונים נכתבים, הם נכתבים תחילה לגרסה המקומית הזו. לאחר מכן, לקוח Firebase מסנכרן את הנתונים האלה עם שרתי מסד הנתונים המרוחקים ועם לקוחות אחרים על בסיס "המאמץ הטוב ביותר".

כתוצאה מכך, כל הכתיבה למסד הנתונים מפעילה אירועים מקומיים באופן מיידי, לפני כל כתיבה של נתונים לשרת. משמעות הדבר היא שהאפליקציה שלך נשארת מגיבה ללא קשר לאחזור הרשת או הקישוריות.

ברגע שהקישוריות מתבססת מחדש, האפליקציה שלך מקבלת את סט האירועים המתאים כך שהלקוח מסתנכרן עם מצב השרת הנוכחי, ללא צורך בכתיבת קוד מותאם אישית.

נדבר יותר על התנהגות לא מקוונת במידע נוסף על יכולות מקוונות ולא מקוונות ..

הצעדים הבאים