שיטות מומלצות לשימוש ב-IndexedDB

שיטות מומלצות לסנכרון מצב האפליקציה בין IndexedDB ספריות ניהול פופולריות של מצבים.

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

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

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

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

שמירה על יכולת החיזוי של האפליקציה

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

לא כל דבר אפשר לאחסן ב-IndexedDB בכל הפלטפורמות

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

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

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

כדי להמיר ArrayBuffer ל-Blob, צריך להשתמש ב-constructor של Blob.

function arrayBufferToBlob(buffer, type) {
  return new Blob([buffer], { type: type });
}

הכיוון השני מעורב קצת יותר, והוא תהליך אסינכרוני. אפשר להשתמש אובייקט FileReader כדי לקרוא את ה-blob בתור ArrayBuffer. בסיום הקריאה loadend האירוע מופעל בקורא. אפשר לכלול את התהליך הזה ב-Promise כך:

function blobToArrayBuffer(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.addEventListener('loadend', () => {
      resolve(reader.result);
    });
    reader.addEventListener('error', reject);
    reader.readAsArrayBuffer(blob);
  });
}

הכתיבה לאחסון עשויה להיכשל

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

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

כדי לזהות שגיאות בפעולות של IndexedDB, אפשר להוסיף handler של אירועים עבור האירוע error בכל פעם שיוצרים אובייקט IDBDatabase, IDBTransaction או IDBRequest.

const request = db.open('example-db', 1);
request.addEventListener('error', (event) => {
  console.log('Request error:', request.error);
};

יכול להיות שהמשתמש שינה או מחק נתונים מאוחסנים

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

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

יכול להיות שהנתונים המאוחסנים לא עדכניים

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

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

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

שמירה על ביצועי האפליקציה

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

באופן כללי, פעולות קריאה וכתיבה ב-IndexedDB לא יכולות להיות גדולות יותר מהנדרש עבור הנתונים מתבצעת גישה.

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

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

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

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

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

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

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

מסקנות

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

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

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