נתונים שזמינים במצב אופליין

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

אחסון

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

  • IndexedDB: אפשרות לאחסון אובייקטים ב-NoSQL לנתונים מובְנים ו-blobs (נתונים בינאריים).
  • WebStorage: דרך לאחסן צמדי מחרוזות של מפתח/ערך באמצעות אחסון מקומי או אחסון סשן. היא לא זמינה בהקשר של קובץ שירות (service worker). ה-API הזה סינכרוני ולכן לא מומלץ להשתמש באחסון מורכב של נתונים.
  • אחסון המטמון: כפי שמוסבר במודול השמירה במטמון.

אפשר לנהל את כל נפח האחסון במכשיר באמצעות Storage Manager API בפלטפורמות נתמכות. ה-API של Cache Storage ו-IndexedDB מספקים גישה אסינכרונית לאחסון מתמיד של אפליקציות PWA, וניתן לגשת אליהם מה-thread הראשי, מ-Web worker ומ-Service Workers. לשניהם יש תפקידים חיוניים כדי שאפליקציות PWA יפעלו בצורה אמינה כשהרשת לא יציבה או לא קיימת. מתי כדאי להשתמש בכל אחת מהן?

משתמשים ב-Cache Storage API למשאבי רשת, לדברים שאליהם ניתן לגשת אם מבקשים אותם דרך כתובת URL, כמו HTML , CSS , JavaScript, תמונות, סרטונים ואודיו.

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

IndexedDB

כדי להשתמש ב-IndexedDB, קודם פותחים מסד נתונים. הפעולה הזו יוצרת מסד נתונים חדש אם קיים מסד נתונים כזה. IndexedDB הוא API אסינכרוני, אבל הוא מקבל קריאה חוזרת במקום להחזיר Promise. בדוגמה הבאה נשתמש בספריית idb של ג'ייק ארצ'יבלד, שהיא wrapper קטן בסגנון Promise של IndexedDB. ספריות מסייעות לא נדרשות כדי להשתמש ב-IndexedDB, אבל אם רוצים להשתמש בתחביר Promise, אפשר להשתמש בספרייה idb.

הדוגמה הבאה יוצרת מסד נתונים לשמירת מתכונים לבישול.

יצירה ופתיחה של מסד נתונים

כדי לפתוח מסד נתונים:

  1. אפשר להשתמש בפונקציה openDB כדי ליצור מסד נתונים חדש ב-IndexedDB בשם cookbook. מכיוון שמסדי נתונים של IndexedDB נוצרים גרסאות, צריך להגדיל את מספר הגרסה בכל פעם שמבצעים שינויים במבנה של מסד הנתונים. הפרמטר השני הוא גרסת מסד הנתונים. בדוגמה שלמעלה, הערך הוא 1.
  2. אובייקט אתחול שמכיל קריאה חוזרת של upgrade() מועבר אל openDB(). מתבצעת קריאה לפונקציית הקריאה החוזרת כאשר מסד הנתונים מותקן בפעם הראשונה או בעת שדרוג לגרסה חדשה. הפונקציה הזו היא המקום היחיד שבו יכולות להתרחש פעולות. הפעולות יכולות לכלול יצירה של מאגרי אובייקטים חדשים (המבנים ש-IndexedDB משתמש בהם לארגון נתונים) או אינדקסים (שרוצים לחפש בהם). זה גם המקום שבו צריכה להתבצע העברת נתונים. בדרך כלל, הפונקציה upgrade() מכילה הצהרה switch ללא הצהרות break, כדי לאפשר לכל שלב להתרחש לפי הסדר, בהתאם לגרסה הישנה של מסד הנתונים.
import { openDB } from 'idb';

async function createDB() {
  // Using https://github.com/jakearchibald/idb
  const db = await openDB('cookbook', 1, {
    upgrade(db, oldVersion, newVersion, transaction) {
      // Switch over the oldVersion, *without breaks*, to allow the database to be incrementally upgraded.
    switch(oldVersion) {
     case 0:
       // Placeholder to execute when database is created (oldVersion is 0)
     case 1:
       // Create a store of objects
       const store = db.createObjectStore('recipes', {
         // The `id` property of the object will be the key, and be incremented automatically
           autoIncrement: true,
           keyPath: 'id'
       });
       // Create an index called `name` based on the `type` property of objects in the store
       store.createIndex('type', 'type');
     }
   }
  });
}

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

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

ב-Safari וב-Chrome מוצגים תכנים של IndexedDB.

הוספת נתונים

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

  1. מתחילים עסקה עם המאפיין mode שמוגדר ל-readwrite.
  2. מוצאים את מאגר האובייקטים שבו מוסיפים נתונים.
  3. צריך להפעיל את add() עם הנתונים שנשמרים. השיטה מקבלת נתונים בצורת מילון (כצמדי מפתח/ערך) ומוסיפה אותם למאגר האובייקטים. צריך להיות אפשרות לשכפל את המילון באמצעות שכפול מובנה. אם רוצים לעדכן אובייקט קיים, צריך לקרוא ל-method put().

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

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

// Using https://github.com/jakearchibald/idb
async function addData() {
  const cookies = {
      name: "Chocolate chips cookies",
      type: "dessert",
        cook_time_minutes: 25
  };
  const tx = await db.transaction('recipes', 'readwrite');
  const store = tx.objectStore('recipes');
  store.add(cookies);
  await tx.done;
}

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

הנתונים בתהליך אחזור

כך מקבלים נתונים מ-IndexedDB:

  1. מתחילים עסקה ומציינים את החנות או החנויות של האובייקטים, ואם רוצים, גם את סוג העסקה.
  2. צריך להתקשר אל objectStore() מהעסקה הזו. חשוב לציין את שם מאגר האובייקטים.
  3. עליך להתקשר אל get() עם המפתח הרצוי. כברירת מחדל, החנות משתמשת במפתח שלה כאינדקס.
// Using https://github.com/jakearchibald/idb
async function getData() {
  const tx = await db.transaction('recipes', 'readonly')
  const store = tx.objectStore('recipes');
// Because in our case the `id` is the key, we would
// have to know in advance the value of the id to
// retrieve the record
  const value = await store.get([id]);
}

מנהל האחסון

חשוב במיוחד לדעת איך לנהל את האחסון של PWA כדי לאחסן ולשדר תגובות רשת בצורה נכונה.

קיבולת האחסון משותפת בין כל אפשרויות האחסון, כולל Cache Storage, IndexedDB, Web Storage ואפילו קובץ Service Worker ורכיבי התלות שלו. עם זאת, נפח האחסון הזמין משתנה בין דפדפנים. סביר להניח שלא ייגמר לך המקום, אתרים יכולים לאחסן מגה-בייט ואפילו ג'יגה-בייט של נתונים בדפדפנים מסוימים. למשל, Chrome מאפשר לדפדפן לנצל עד 80% מנפח האחסון הכולל, ומקור ספציפי יכול לנצל עד 60% מנפח הדיסק הכולל. בדפדפנים שתומכים ב-Storage API, אפשר לראות כמה נפח אחסון עדיין זמין לאפליקציה, למכסה שלה ולשימוש בה. בדוגמה הבאה נעשה שימוש ב-Storage API כדי לקבל אומדן של המכסות והשימוש, ואז חישוב של האחוז שנוצל ושל הבייטים שנותרו. שימו לב שהפונקציה navigator.storage מחזירה מופע של StorageManager. יש ממשק נפרד של Storage וקל להתבלבל ביניהם.

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

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

כלי פיתוח ל-Chrome באפליקציה, הקטע 'ניקוי האחסון'

ב-Firefox וב-Safari אין מסך סיכום שבו אפשר לראות את כל מכסת האחסון והשימושים במקור הנוכחי.

עקביות נתונים

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

כדי לבקש נפח אחסון מתמיד, צריך להתקשר אל StorageManager.persist(). כמו תמיד, אפשר לגשת לממשק StorageManager דרך הנכס navigator.storage.

async function persistData() {
  if (navigator.storage && navigator.storage.persist) {
    const result = await navigator.storage.persist();
    console.log(`Data persisted: ${result}`);
}

אפשר גם לבדוק אם כבר הוענק נפח אחסון מתמיד במקור הנוכחי בקריאה ל-StorageManager.persisted(). Firefox מבקש מהמשתמש הרשאה להשתמש באחסון מתמיד. דפדפנים המבוססים על Chromium מספקים או דוחים עקביות על סמך היוריסטיקה כדי לקבוע את חשיבות התוכן בשביל המשתמש. אחד הקריטריונים ל-Google Chrome הוא, לדוגמה, התקנת PWA. אם המשתמש התקין סמל של PWA במערכת ההפעלה, הדפדפן עשוי להעניק נפח אחסון מתמיד.

Mozilla Firefox מבקש מהמשתמש הרשאה לשמירת האחסון.

תמיכה בדפדפן API

אחסון באינטרנט

תמיכה בדפדפן

  • Chrome: 4.
  • קצה: 12.
  • Firefox: 3.5.
  • Safari: 4.

מקור

גישה למערכת הקבצים

תמיכה בדפדפן

  • Chrome: 86.
  • קצה: 86.
  • Firefox: 111.
  • Safari: 15.2.

מקור

מנהל אחסון

תמיכה בדפדפן

  • Chrome: 55.
  • קצה: 79.
  • Firefox: 57.
  • Safari: 15.2.

מקור

משאבים