แนวทางปฏิบัติแนะนำสำหรับการใช้ IndexedDB

เรียนรู้แนวทางปฏิบัติแนะนำสำหรับการซิงค์สถานะแอปพลิเคชันระหว่าง IndexedDB กับไลบรารีการจัดการสถานะยอดนิยม

เมื่อผู้ใช้โหลดเว็บไซต์หรือแอปพลิเคชันเป็นครั้งแรก มักจะมีงานพอสมควรใน สร้างสถานะแอปพลิเคชันเริ่มต้นที่ใช้ในการแสดงผล UI ตัวอย่างเช่น บางครั้ง แอปต้องตรวจสอบสิทธิ์ฝั่งไคลเอ็นต์ของผู้ใช้ จากนั้นส่งคำขอ API หลายรายการก่อนจะมีคำขอทั้งหมด ข้อมูลที่จำเป็นต้องแสดงบนหน้าเว็บ

กำลังจัดเก็บสถานะแอปพลิเคชันใน IndexedDB ก็เป็นวิธีที่ยอดเยี่ยมในการเร่งความเร็ว เวลาที่ใช้ในการโหลดสำหรับการเข้าชมซ้ำ จากนั้นแอปสามารถซิงค์กับบริการ API ในเบื้องหลัง และอัปเดต UI ด้วยข้อมูลใหม่แบบ Lazy Loading โดยใช้ ไม่อัปเดตขณะตรวจสอบใหม่

การใช้งานที่ดีอีกอย่างสำหรับ IndexedDB คือการจัดเก็บเนื้อหาที่ผู้ใช้สร้างขึ้น ไม่ว่าจะเป็นการจัดเก็บชั่วคราว ก่อนที่จะถูกอัปโหลดไปยังเซิร์ฟเวอร์ หรือเป็นแคชฝั่งไคลเอ็นต์ของข้อมูลระยะไกล หรือแน่นอน ทั้ง 2 อย่าง

อย่างไรก็ตาม เมื่อใช้ IndexedDB มีสิ่งสำคัญหลายอย่างที่ต้องคำนึงถึงว่า นักพัฒนาซอฟต์แวร์ที่เพิ่งเริ่มใช้ API จะเห็นได้อย่างชัดเจน บทความนี้จะตอบคำถามที่พบบ่อยและ กล่าวถึงสิ่งสำคัญที่สุดที่ควรคำนึงถึงเมื่อเก็บข้อมูลใน IndexedDB อย่างต่อเนื่อง

การทำให้แอปของคุณคาดเดาได้

ความซับซ้อนมากมายเกี่ยวกับ IndexedDB เกิดจากข้อเท็จจริงที่ว่ามีปัจจัยหลายอย่างที่คุณ (นักพัฒนาซอฟต์แวร์) ไม่มีสิทธิ์ควบคุม ส่วนนี้จะอธิบายปัญหามากมายที่คุณต้องคำนึงถึง เมื่อทำงานกับ IndexedDB

ข้อมูลบางอย่างอาจเก็บไว้ใน IndexedDB บางแพลตฟอร์มเท่านั้น

ถ้าคุณจัดเก็บไฟล์ขนาดใหญ่ที่ผู้ใช้สร้างขึ้น เช่น รูปภาพหรือวิดีโอ คุณอาจลองจัดเก็บ เป็นออบเจ็กต์ File หรือ Blob วิธีนี้ได้ผลในบางแพลตฟอร์มแต่ล้มเหลวในบางแพลตฟอร์ม เปิด Safari โดยเฉพาะอย่างยิ่ง iOS ไม่สามารถจัดเก็บ Blob ใน IndexedDB

โชคดีที่การแปลง Blob เป็น ArrayBuffer ก็ไม่ยากเกินไป และในทางกลับกันด้วย การจัดเก็บ รองรับ ArrayBuffer ใน IndexedDB แล้ว

อย่างไรก็ตาม โปรดทราบว่า Blob จะมีประเภท MIME แต่ ArrayBuffer ไม่มี คุณจะต้องทำดังนี้ จัดเก็บประเภทไว้ข้างบัฟเฟอร์เพื่อทำ Conversion ได้อย่างถูกต้อง

หากต้องการแปลง ArrayBuffer เป็น Blob เพียงใช้ตัวสร้าง 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 ซึ่งหมายความว่าคุณควรเก็บสถานะของแอปพลิเคชันไว้ในหน่วยความจำ (ใน ไม่จัดเก็บไว้) เพื่อให้ UI ไม่เสียเมื่อทำงานในโหมดการเรียกดูแบบส่วนตัวหรือเมื่อ พื้นที่เก็บข้อมูลไม่พร้อมให้บริการ (แม้ว่าฟีเจอร์บางอย่างของแอปที่ต้องใช้พื้นที่เก็บข้อมูลจะไม่มี งาน)

คุณตรวจจับข้อผิดพลาดในการดำเนินงาน IndexedDB ได้โดยการเพิ่มเครื่องจัดการเหตุการณ์สำหรับเหตุการณ์ error ทุกครั้งที่คุณสร้างออบเจ็กต์ IDBDatabase, IDBTransaction หรือ IDBRequest

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

ผู้ใช้อาจแก้ไขหรือลบข้อมูลที่จัดเก็บไว้

ฐานข้อมูลฝั่งไคลเอ็นต์จะแตกต่างจากฐานข้อมูลฝั่งเซิร์ฟเวอร์ที่คุณสามารถจำกัดการเข้าถึงที่ไม่ได้รับอนุญาต เข้าถึงส่วนขยายของเบราว์เซอร์และเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ได้ และผู้ใช้ก็สามารถล้างได้

ถึงแม้ว่าผู้ใช้จะแก้ไขข้อมูลที่จัดเก็บภายในเครื่อง แต่ไม่ใช่เรื่องที่พบได้บ่อยสำหรับผู้ใช้ เพื่อล้างออก แอปพลิเคชันของคุณต้องจัดการกับทั้ง 2 กรณีนี้โดยไม่มีข้อผิดพลาด

ข้อมูลที่จัดเก็บไว้อาจล้าสมัย

คล้ายกับส่วนก่อนหน้านี้ แม้ว่าผู้ใช้จะไม่ได้แก้ไขข้อมูลเอง อาจเป็นไปได้ว่าข้อมูลที่ตนมีอยู่ในพื้นที่เก็บข้อมูลถูกเขียนโดยโค้ดเวอร์ชันเก่าของคุณ ซึ่งอาจเป็น ที่มีข้อบกพร่อง

IndexedDB มีการสนับสนุนในตัวสำหรับเวอร์ชันสคีมาและการอัปเกรดผ่าน IDBOpenDBRequest.onupgradeneeded() วิธีการ อย่างไรก็ตาม คุณยังต้องเขียนโค้ดอัปเกรดในลักษณะที่สามารถจัดการผู้ใช้ได้ มาจากเวอร์ชันก่อนหน้า (รวมถึงเวอร์ชันที่มีข้อบกพร่อง)

การทดสอบ 1 หน่วยมีประโยชน์มากเนื่องจากมักจะไม่สามารถทดสอบด้วยตนเองได้ทั้งหมด อัปเกรดเส้นทางและเคส

ทำให้แอปของคุณมีประสิทธิภาพอยู่เสมอ

หนึ่งในคุณลักษณะสำคัญของ IndexedDB คือ API แบบอะซิงโครนัส แต่อย่าหลอกให้คุณหลงเชื่อ โดยคิดว่าคุณไม่จำเป็นต้องกังวลเรื่องประสิทธิภาพในขณะใช้งาน มีหลายกรณีที่ การใช้งานที่ไม่เหมาะสมจะยังคงบล็อกเทรดหลักได้ ซึ่งอาจทำให้เกิดการกระตุกและไม่ตอบสนอง

ตามกฎทั่วไป การอ่านและเขียนไปยัง IndexedDB ไม่ควรมีขนาดใหญ่กว่าที่กำหนดสำหรับข้อมูล มีการเข้าถึง

แม้ว่า IndexedDB จะช่วยให้จัดเก็บอ็อบเจ็กต์ขนาดใหญ่ที่ซ้อนไว้เป็นระเบียนเดียวได้ (และการทำเช่นนี้คือ ค่อนข้างสะดวกในมุมมองของนักพัฒนาซอฟต์แวร์) ก็ควรหลีกเลี่ยงแนวทางปฏิบัตินี้ เนื่องจากเมื่อ IndexedDB จัดเก็บออบเจ็กต์ ออบเจ็กต์นั้นต้องสร้าง การโคลนที่มีโครงสร้าง ของออบเจ็กต์นั้น และกระบวนการโคลนที่มีโครงสร้างจะเกิดขึ้นในเทรดหลัก ยิ่ง ยิ่งบล็อกนานขึ้นเท่านั้น

ซึ่งก็นำมาซึ่งความท้าทายบางอย่างเมื่อทำการวางแผนรักษาสถานะแอปพลิเคชันให้กับ IndexedDB อย่างต่อเนื่อง ของไลบรารีการจัดการสถานะที่ได้รับความนิยม (เช่น Redux) ทำงานโดยการจัดการ แผนผังสถานะทั้งหมดเป็นออบเจ็กต์ JavaScript รายการเดียว

ขณะที่การจัดการสถานะในลักษณะนี้มีประโยชน์มากมาย (เช่น ทำให้การบอกเหตุผลเกี่ยวกับโค้ดเป็นเรื่องง่ายและ ) และในขณะที่การจัดเก็บแผนผังสถานะทั้งหมดเป็นระเบียนเดียวใน IndexedDB อาจเป็น น่าสนใจและสะดวก การทำเช่นนี้หลังทุกๆ การเปลี่ยนแปลง (แม้ว่าจะถูกควบคุม/ดีเบา) จะส่งผลให้ การบล็อกเทรดหลักโดยไม่จำเป็น ซึ่งจะเพิ่มโอกาสในการเขียนข้อผิดพลาด และใน ในบางกรณี แท็บเบราว์เซอร์อาจขัดข้องหรือไม่ตอบสนองได้

แทนที่จะจัดเก็บต้นไม้ของรัฐทั้งหมดในบันทึกเดียว คุณควรแยกต้นไม้ออกเป็นส่วนๆ และอัปเดตระเบียนที่มีการเปลี่ยนแปลงจริงเท่านั้น

และจะเป็นเช่นนี้ในกรณีที่คุณจัดเก็บรายการขนาดใหญ่ เช่น รูปภาพ เพลง หรือวิดีโอไว้ใน IndexedDB จัดเก็บสินค้าแต่ละรายการ ด้วยคีย์ของตัวเองแทนที่จะอยู่ภายในออบเจ็กต์ขนาดใหญ่ คุณจึงเรียกข้อมูลที่มีโครงสร้างได้ โดยไม่ต้องจ่ายค่าใช้จ่ายในการเรียกไฟล์ไบนารีด้วย

กฎข้อนี้ไม่ใช่กฎที่ห้ามทำในทุกกรณีเช่นเดียวกับแนวทางปฏิบัติแนะนำส่วนใหญ่ ในกรณีที่ไม่สามารถ แยกอ็อบเจกต์สถานะขึ้นมาแล้วเขียนชุดการเปลี่ยนแปลงที่น้อยที่สุด โดยจะแบ่งข้อมูลออกเป็นแผนผังย่อย และการเขียนเฉพาะข้อความเหล่านั้นก็ยังดีกว่าที่จะเขียนทั้งแผนผังรัฐเสมอ เล็กน้อย ดีกว่าไม่ปรับปรุงเลย

สุดท้ายนี้ คุณควรวัดผลประสิทธิภาพอยู่เสมอ ของโค้ดที่คุณเขียน แม้จะเป็นความจริงที่การเขียนขนาดเล็กไปยัง IndexedDB จะทำงานได้ดีกว่าการค้นหาขนาดใหญ่ จะมีความสำคัญก็ต่อเมื่อการเขียนไปยัง IndexedDB ที่แอปพลิเคชันของคุณกำลังทำอยู่ งานที่ใช้เวลานาน ที่บล็อกเทรดหลัก และทำให้ประสบการณ์ของผู้ใช้แย่ลง เป็นสิ่งสำคัญที่คุณจะต้องวัดผลเพื่อให้คุณ ทำความเข้าใจสิ่งที่คุณกำลังเพิ่มประสิทธิภาพ

บทสรุป

นักพัฒนาซอฟต์แวร์สามารถใช้ประโยชน์จากกลไกพื้นที่เก็บข้อมูลของไคลเอ็นต์ เช่น IndexedDB เพื่อปรับปรุงประสบการณ์ของผู้ใช้สำหรับ โดยไม่เพียงแค่ยืนยันสถานะในเซสชันต่างๆ เท่านั้น แต่ยังรวมถึงการลดเวลา ใช้เพื่อโหลดสถานะเริ่มต้นเมื่อเข้าชมซ้ำ

ขณะที่การใช้ IndexedDB อย่างถูกต้องจะสามารถปรับปรุงประสบการณ์ของผู้ใช้ได้อย่างมาก การใช้อย่างไม่ถูกต้องหรือ การไม่สามารถจัดการกรณีที่เกิดข้อผิดพลาดอาจทำให้แอปเสียหายและผู้ใช้ไม่พอใจ

เนื่องจากพื้นที่เก็บข้อมูลของไคลเอ็นต์เกี่ยวข้องกับหลายปัจจัยที่อยู่นอกการควบคุมของคุณ โค้ดของคุณจึงสำคัญอย่างยิ่ง มีการทดสอบและจัดการข้อผิดพลาดได้อย่างเหมาะสม แม้แต่ข้อผิดพลาดที่อาจดูไม่น่าจะเกิดขึ้นในตอนแรกก็ตาม