Các phương pháp hay nhất để sử dụng IndexedDB

Tìm hiểu các phương pháp hay nhất để đồng bộ hoá trạng thái ứng dụng giữa IndexedDB, một thư viện quản lý trạng thái phổ biến.

Khi người dùng tải một trang web hoặc ứng dụng lần đầu tiên, thường sẽ có một lượng công việc tương đối lớn tạo trạng thái ứng dụng ban đầu dùng để kết xuất giao diện người dùng. Ví dụ: đôi khi ứng dụng cần xác thực phía máy khách của người dùng, sau đó thực hiện một số yêu cầu API trước khi có tất cả dữ liệu cần hiển thị trên trang.

Lưu trữ trạng thái của ứng dụng trong IndexedDB có thể là cách tuyệt vời để tăng tốc thời gian tải cho các lượt truy cập lặp lại. Sau đó, ứng dụng này có thể đồng bộ hoá với mọi dịch vụ API ở chế độ nền và cập nhật từng phần dữ liệu mới trên giao diện người dùng, sử dụng cũ trong khi xác thực lại.

Một cách sử dụng hay khác của IndexedDB là lưu trữ nội dung do người dùng tạo, dưới dạng cửa hàng tạm thời trước khi nó được tải lên máy chủ hoặc dưới dạng bộ nhớ đệm phía máy khách của dữ liệu từ xa – hoặc tất nhiên là cả hai.

Tuy nhiên, khi sử dụng IndexedDB, có nhiều điều quan trọng cần xem xét có thể không phải là rõ ràng ngay lập tức đối với các nhà phát triển mới sử dụng API. Bài viết này giải đáp các thắc mắc thường gặp và thảo luận một số điều quan trọng nhất cần lưu ý khi duy trì dữ liệu trong IndexedDB.

Giúp ứng dụng của bạn dễ dự đoán

Rất nhiều sự phức tạp xung quanh IndexedDB xuất phát từ thực tế là có rất nhiều yếu tố mà bạn (nhà phát triển) không có quyền kiểm soát. Phần này khám phá nhiều vấn đề mà bạn phải lưu ý khi làm việc với IndexedDB.

Không phải mọi nội dung đều có thể được lưu trữ trong IndexedDB trên tất cả các nền tảng

Nếu đang lưu trữ các tệp lớn do người dùng tạo (chẳng hạn như hình ảnh hoặc video) thì bạn có thể thử lưu trữ chúng dưới dạng đối tượng File hoặc Blob. Cách này sẽ hoạt động trên một số nền tảng nhưng không hoạt động trên các nền tảng khác. Bật Safari Cụ thể, iOS không thể lưu trữ Blob trong IndexedDB.

May mắn là việc chuyển đổi Blob thành ArrayBuffer không quá khó và ngược lại. Đang lưu trữ Các ArrayBuffer trong IndexedDB được hỗ trợ rất tốt.

Tuy nhiên, hãy nhớ rằng Blob có loại MIME trong khi ArrayBuffer thì không. Bạn sẽ cần lưu trữ loại cùng với vùng đệm để thực hiện chuyển đổi một cách chính xác.

Để chuyển đổi ArrayBuffer thành Blob, bạn chỉ cần sử dụng hàm khởi tạo Blob.

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

Hướng còn lại liên quan nhiều hơn một chút và là một quá trình không đồng bộ. Bạn có thể sử dụng FileReader để đọc blob dưới dạng ArrayBuffer. Khi đọc xong, loadend sự kiện được kích hoạt trên trình đọc. Bạn có thể gói quy trình này trong một Promise như sau:

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

Không ghi được vào bộ nhớ

Lỗi khi ghi vào IndexedDB có thể xảy ra vì nhiều lý do và trong một số trường hợp, nằm ngoài tầm kiểm soát của bạn với tư cách là nhà phát triển. Ví dụ: một số trình duyệt hiện không cho phép ghi vào IndexedDB khi ở chế độ duyệt web riêng tư. Cũng có khả năng người dùng đang sử dụng một thiết bị sắp hết dung lượng ổ đĩa và trình duyệt sẽ hạn chế bạn lưu trữ bất kỳ thông tin nào.

Do đó, điều cực kỳ quan trọng là bạn phải luôn triển khai cách xử lý lỗi thích hợp trong Mã IndexedDB. Điều này cũng có nghĩa là tốt hơn hết nên giữ trạng thái của ứng dụng trong bộ nhớ (trong ngoài việc lưu trữ dữ liệu), nhờ đó, giao diện người dùng không bị lỗi khi chạy ở chế độ duyệt web riêng tư hoặc khi không dùng được dung lượng lưu trữ (ngay cả khi một số tính năng khác của ứng dụng cần có dung lượng lưu trữ sẽ không dùng được cơ quan).

Bạn có thể phát hiện lỗi trong các thao tác IndexedDB bằng cách thêm trình xử lý sự kiện cho sự kiện error bất cứ khi nào bạn tạo đối tượng IDBDatabase, IDBTransaction hoặc IDBRequest.

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

Dữ liệu được lưu trữ có thể đã bị người dùng sửa đổi hoặc xoá

Không giống như cơ sở dữ liệu phía máy chủ mà bạn có thể hạn chế quyền truy cập trái phép, cơ sở dữ liệu phía máy khách các tiện ích của trình duyệt và công cụ cho nhà phát triển có thể truy cập đồng thời người dùng có thể xoá chúng.

Mặc dù người dùng có thể không sửa đổi dữ liệu được lưu trữ trên máy của họ nhưng việc này khá phổ biến đối với người dùng để xóa dữ liệu đó. Điều quan trọng là ứng dụng của bạn có thể xử lý cả hai trường hợp này mà không gặp lỗi.

Dữ liệu được lưu trữ có thể đã lỗi thời

Tương tự như phần trước, ngay cả khi người dùng không tự sửa đổi dữ liệu, có thể dữ liệu họ có trong bộ nhớ được ghi bởi một phiên bản mã cũ của bạn, có thể phiên bản có lỗi.

IndexedDB tích hợp sẵn hỗ trợ cho các phiên bản giản đồ và nâng cấp thông qua IDBOpenDBRequest.onupgradeneeded() phương thức; tuy nhiên, bạn vẫn cần phải viết mã nâng cấp theo cách mà mã đó có thể xử lý người dùng đến từ phiên bản trước (bao gồm cả phiên bản có lỗi).

Phương pháp kiểm thử đơn vị có thể rất hữu ích trong trường hợp này, vì thường không thể kiểm thử thủ công tất cả trường hợp và đường dẫn nâng cấp.

Duy trì hiệu suất của ứng dụng

Một trong những tính năng chính của IndexedDB là API không đồng bộ, nhưng đừng để điều đó lừa bạn nghĩ rằng bạn không cần lo lắng về hiệu suất khi sử dụng. Có một số trường hợp mà Việc sử dụng không đúng cách vẫn có thể chặn luồng chính, dẫn đến hiện tượng giật và phản hồi.

Theo quy tắc chung, số lượt đọc và ghi vào IndexedDB không được lớn hơn yêu cầu đối với dữ liệu truy cập.

Mặc dù IndexedDB giúp bạn lưu trữ các đối tượng lớn, lồng nhau dưới dạng bản ghi duy nhất (và làm như vậy thực sự khá thuận tiện từ góc độ nhà phát triển), nên tránh thực hiện này. Chiến lược phát hành đĩa đơn lý do là vì khi IndexedDB lưu trữ một đối tượng, trước tiên nó phải tạo một bản sao có cấu trúc của đối tượng đó và quá trình nhân bản có cấu trúc diễn ra trên luồng chính. Khoảng không quảng cáo thì thời gian chặn sẽ càng dài.

Điều này đặt ra một số thách thức khi lập kế hoạch cách duy trì trạng thái ứng dụng cho IndexedDB, vì hầu hết của các thư viện quản lý trạng thái phổ biến (như Redux) hoạt động bằng cách quản lý toàn bộ cây trạng thái dưới dạng một đối tượng JavaScript duy nhất.

Tuy việc quản lý trạng thái theo cách này mang lại nhiều lợi ích (ví dụ: giúp mã của bạn dễ hiểu và gỡ lỗi) và mặc dù chỉ cần lưu trữ toàn bộ cây trạng thái dưới dạng một bản ghi duy nhất trong IndexedDB thực sự hấp dẫn và thuận tiện, thực hiện điều này sau mỗi thay đổi (ngay cả khi được điều tiết/bị nảy ra) sẽ dẫn đến chặn luồng chính một cách không cần thiết, điều này sẽ làm tăng khả năng xảy ra lỗi ghi và một số trường hợp, thậm chí nó sẽ khiến thẻ trình duyệt gặp sự cố hoặc không phản hồi.

Thay vì lưu trữ toàn bộ cây trạng thái trong một bản ghi, bạn nên chia nhỏ nó thành từng bản ghi riêng lẻ và chỉ cập nhật những bản ghi thực sự thay đổi.

Điều này cũng đúng nếu bạn lưu trữ các mục lớn như hình ảnh, nhạc hoặc video trong IndexedDB. Lưu trữ từng mặt hàng bằng khoá riêng thay vì bên trong một đối tượng lớn hơn để bạn có thể truy xuất dữ liệu có cấu trúc mà không phải trả chi phí truy xuất tệp nhị phân.

Giống như hầu hết các phương pháp hay nhất, đây không phải là một quy tắc tất cả. Trong trường hợp không thể chia nhỏ một đối tượng trạng thái và chỉ cần viết tập hợp thay đổi tối thiểu, chia dữ liệu thành các cây con và chỉ viết các mã đó vẫn tốt hơn là luôn viết toàn bộ cây trạng thái. Ít cải thiện vẫn tốt hơn là không cải thiện.

Cuối cùng, bạn nên luôn đo lường tác động đến hiệu suất của mã bạn viết. Mặc dù đúng là ghi dữ liệu nhỏ vào IndexedDB sẽ hoạt động tốt hơn ghi vào mã lớn ghi, điều này chỉ quan trọng nếu việc ghi vào IndexedDB mà ứng dụng của bạn đang thực hiện thực sự dẫn đến tác vụ mất nhiều thời gian chặn luồng chính và làm giảm trải nghiệm người dùng. Điều quan trọng là bạn phải đo lường hiểu mục tiêu bạn đang tối ưu hoá.

Kết luận

Nhà phát triển có thể tận dụng cơ chế lưu trữ ứng dụng như IndexedDB để cải thiện trải nghiệm người dùng của ứng dụng của họ bằng cách không chỉ duy trì trạng thái qua các phiên mà còn giảm thời gian để tải trạng thái ban đầu trong các lượt truy cập lặp lại.

Mặc dù việc sử dụng IndexedDB đúng cách có thể cải thiện đáng kể trải nghiệm người dùng, nhưng việc sử dụng IndexedDB không đúng cách hoặc không xử lý được các trường hợp lỗi có thể khiến ứng dụng bị hỏng và người dùng không hài lòng.

Vì bộ nhớ của ứng dụng liên quan đến nhiều yếu tố nằm ngoài tầm kiểm soát của bạn, nên điều quan trọng là mã của bạn phải ổn định đã kiểm tra và xử lý lỗi đúng cách, ngay cả những lỗi ban đầu có vẻ như không xảy ra.