Bộ nhớ dành cho web

Có nhiều tuỳ chọn khác nhau để lưu trữ dữ liệu trong trình duyệt. Giải pháp nào phù hợp nhất với nhu cầu của bạn?

Kết nối Internet có thể không ổn định hoặc không hoạt động khi di chuyển. Đó là lý do tại sao tính năng hỗ trợ ngoại tuyến và hiệu suất đáng tin cậy là các tính năng phổ biến trong các ứng dụng web tiến bộ. Ngay cả trong môi trường không dây hoàn hảo, việc sử dụng hợp lý khả năng lưu vào bộ nhớ đệm và các kỹ thuật lưu trữ khác có thể cải thiện đáng kể trải nghiệm người dùng. Có một số cách để lưu các tài nguyên ứng dụng tĩnh của bạn vào bộ nhớ đệm (HTML, JavaScript, CSS, hình ảnh, v.v.) và dữ liệu (dữ liệu người dùng, tin bài, v.v.). Nhưng giải pháp nào là tốt nhất? Bạn có thể lưu trữ bao nhiêu? Làm cách nào để bạn ngăn nhóm này bị loại?

Tôi nên sử dụng gì?

Dưới đây là đề xuất chung để lưu trữ tài nguyên:

IndexedDB và Cache Storage API được hỗ trợ trong mọi trình duyệt hiện đại. Cả hai đều không đồng bộ và sẽ không chặn luồng chính. Đối tượng window, trình thực thi web và trình chạy dịch vụ có thể truy cập vào các lớp này, giúp bạn dễ dàng sử dụng ở bất cứ đâu trong mã.

Còn các cơ chế lưu trữ khác thì sao?

Có một số cơ chế lưu trữ khác trong trình duyệt, nhưng các cơ chế này được sử dụng hạn chế và có thể gây ra những vấn đề nghiêm trọng về hiệu suất.

SessionStorage là một thẻ dành riêng cho thẻ và nằm trong phạm vi thời gian hoạt động của thẻ. Điều này có thể hữu ích khi lưu trữ một lượng nhỏ thông tin cụ thể về phiên, ví dụ: khoá IndexedDB. Bạn nên thận trọng khi dùng hàm này vì nó có tính đồng bộ và sẽ chặn luồng chính. Kích thước này có giới hạn khoảng 5 MB và chỉ có thể chứa các chuỗi. Vì hoạt động tuỳ theo thẻ nên trình thực thi web hoặc trình chạy dịch vụ không thể truy cập vào biểu tượng này.

Bạn nên tránh sử dụng LocalStorage vì tính năng này có tính đồng bộ và sẽ chặn luồng chính. Tệp này có kích thước khoảng 5 MB và chỉ có thể chứa các chuỗi. Bạn không thể truy cập vào LocalStorage để sử dụng trình chạy web hoặc trình chạy dịch vụ.

Cookie cũng có công dụng của nó, nhưng không nên dùng để lưu trữ. Cookie được gửi cùng với mọi yêu cầu HTTP, vì vậy, việc lưu trữ bất kỳ dữ liệu nào có nhiều hơn một lượng nhỏ dữ liệu sẽ làm tăng đáng kể kích thước của mỗi yêu cầu web. Các tham số này đồng bộ và trình thực thi web không thể truy cập được. Giống như LocalStorage và SessionStorage, cookie chỉ giới hạn ở chuỗi.

File System API (API Hệ thống tệp) và API FileWriter cung cấp các phương thức đọc và ghi tệp vào hệ thống tệp hộp cát. Mặc dù không đồng bộ, nhưng bạn không nên dùng API này vì API này chỉ có trong các trình duyệt dựa trên Chromium.

File System Access API (API Truy cập hệ thống tệp) được thiết kế nhằm giúp người dùng dễ dàng đọc và chỉnh sửa tệp trên hệ thống tệp cục bộ của họ. Người dùng phải cấp quyền thì một trang mới có thể đọc hoặc ghi vào bất kỳ tệp cục bộ nào, đồng thời các quyền không được duy trì trong các phiên.

Bạn không nên sử dụng WebSQL và mức sử dụng hiện tại nên được di chuyển sang IndexedDB. Dịch vụ hỗ trợ đã bị loại bỏ khỏi hầu hết các trình duyệt chính. W3C đã ngừng duy trì quy cách Web SQL từ năm 2010 mà không có kế hoạch cập nhật tiếp theo.

Bạn không nên sử dụng Bộ nhớ đệm của ứng dụng và mức sử dụng hiện tại nên được di chuyển sang trình thực thi dịch vụ và API Bộ nhớ đệm. Chúng tôi đã ngừng sử dụng tính năng này và dịch vụ hỗ trợ sẽ bị loại bỏ khỏi các trình duyệt trong tương lai.

Tôi có thể lưu trữ bao nhiêu?

Tóm lại là nhiều, ít nhất là vài trăm megabyte và có thể có hàng trăm gigabyte trở lên. Các cách triển khai trình duyệt sẽ khác nhau, nhưng dung lượng lưu trữ có sẵn thường dựa trên dung lượng lưu trữ có sẵn trên thiết bị.

  • Chrome cho phép trình duyệt sử dụng tới 80% tổng dung lượng ổ đĩa. Nguồn gốc có thể sử dụng tới 60% tổng dung lượng ổ đĩa. Bạn có thể sử dụng API StorageManager để xác định hạn mức tối đa hiện có. Các trình duyệt dựa trên Chromium khác có thể khác.
    • Ở chế độ ẩn danh, Chrome sẽ giảm dung lượng bộ nhớ mà một nguồn gốc có thể sử dụng xuống khoảng 5% tổng dung lượng ổ đĩa.
    • Nếu người dùng bật tính năng "Xoá cookie và dữ liệu trang web khi bạn đóng tất cả cửa sổ" trong Chrome, thì hạn mức bộ nhớ sẽ giảm đáng kể xuống còn tối đa khoảng 300 MB.
    • Xem PR #3896 để biết thông tin chi tiết về cách triển khai của Chrome.
  • Internet Explorer 10 trở lên có thể lưu trữ tối đa 250 MB và sẽ thông báo cho người dùng khi sử dụng hơn 10 MB.
  • Firefox cho phép trình duyệt sử dụng đến 50% dung lượng ổ đĩa trống. Một nhóm eTLD+1 (ví dụ: example.com, www.example.comfoo.bar.example.com) có thể sử dụng tối đa 2 GB. Bạn có thể sử dụng API StorageManager để xác định dung lượng còn trống.
  • Safari (cả máy tính và thiết bị di động) có vẻ như cho phép khoảng 1GB. Khi đạt đến giới hạn, Safari sẽ thông báo cho người dùng và tăng giới hạn lên 200 MB. Tôi không tìm thấy tài liệu chính thức nào về vấn đề này.
    • Nếu bạn thêm một PWA vào màn hình chính trên Safari dành cho thiết bị di động, thì PWA sẽ tạo một vùng chứa bộ nhớ mới và không có dữ liệu nào được chia sẻ giữa PWA và Safari dành cho thiết bị di động. Một khi đạt đến hạn mức cho một PWA đã cài đặt, sẽ không có cách nào để yêu cầu thêm bộ nhớ.

Trước đây, nếu một trang web vượt quá một ngưỡng dữ liệu nhất định được lưu trữ, thì trình duyệt sẽ nhắc người dùng cấp quyền để sử dụng thêm dữ liệu. Ví dụ: nếu nguồn gốc sử dụng nhiều hơn 50 MB, thì trình duyệt sẽ nhắc người dùng cho phép lưu trữ tối đa 100 MB, sau đó yêu cầu lại với mức tăng 50 MB.

Hiện nay, hầu hết các trình duyệt hiện đại sẽ không nhắc người dùng và sẽ cho phép trang web sử dụng hết hạn mức được phân bổ. Có vẻ như ngoại lệ là Safari. Điều này sẽ nhắc khi vượt quá hạn mức bộ nhớ, yêu cầu quyền tăng hạn mức phân bổ. Nếu một nguồn gốc cố gắng sử dụng nhiều hơn hạn mức được phân bổ, thì những lần thử ghi thêm dữ liệu khác sẽ không thành công.

Làm cách nào để kiểm tra dung lượng lưu trữ còn trống?

Trong nhiều trình duyệt, bạn có thể sử dụng API StorageManager để xác định dung lượng bộ nhớ còn trống cho nguồn gốc và dung lượng bộ nhớ mà trình duyệt đó đang sử dụng. API này báo cáo tổng số byte mà IndexedDB và Cache API sử dụng, đồng thời giúp bạn có thể tính toán dung lượng bộ nhớ còn lại gần đúng còn trống.

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

StorageManager chưa được triển khai trong tất cả trình duyệt. Vì vậy, bạn phải có tính năng phát hiện trình quản lý này trước khi sử dụng. Ngay cả khi có sẵn, bạn vẫn phải phát hiện lỗi vượt quá hạn mức (xem bên dưới). Trong một số trường hợp, hạn mức hiện có có thể vượt quá dung lượng bộ nhớ thực tế có sẵn.

Kiểm tra

Trong quá trình phát triển, bạn có thể sử dụng Công cụ cho nhà phát triển của trình duyệt để kiểm tra các loại bộ nhớ khác nhau và dễ dàng xoá tất cả dữ liệu đã lưu trữ.

Một tính năng mới đã được thêm vào Chrome 88, cho phép bạn ghi đè hạn mức bộ nhớ của trang web trong Ngăn bộ nhớ. Tính năng này cho phép bạn mô phỏng nhiều thiết bị và kiểm thử hành vi của ứng dụng trong các tình huống dung lượng ổ đĩa thấp. Chuyển đến Application (Ứng dụng), sau đó nhấp vào Storage (Bộ nhớ), chọn hộp đánh dấu Simulate custom storage hóa (Mô phỏng hạn mức bộ nhớ tuỳ chỉnh) rồi nhập bất kỳ số nào hợp lệ để mô phỏng hạn mức bộ nhớ.

Ngăn Bộ nhớ Công cụ cho nhà phát triển.

Trong khi thực hiện bài viết này, tôi đã viết một công cụ đơn giản để cố gắng sử dụng nhanh nhiều bộ nhớ nhất có thể. Đây là một cách nhanh chóng và dễ dàng để thử nghiệm với các cơ chế lưu trữ khác nhau và xem điều gì xảy ra khi bạn sử dụng hết hạn mức.

Cách xử lý việc vượt quá hạn mức?

Bạn nên làm gì khi vượt quá hạn mức? Điều quan trọng nhất là bạn phải luôn phát hiện và xử lý các lỗi ghi, cho dù đó là QuotaExceededError hay lỗi khác. Sau đó, tuỳ thuộc vào thiết kế của ứng dụng, hãy quyết định cách xử lý. Ví dụ: xoá nội dung mà họ đã không truy cập trong một thời gian dài, xoá dữ liệu dựa trên kích thước hoặc cung cấp cách để người dùng chọn nội dung họ muốn xoá.

Cả IndexedDB và Cache API đều gửi một DOMError có tên là QuotaExceededError khi bạn vượt quá hạn mức hiện có.

IndexedDB

Nếu nguồn gốc vượt quá hạn mức, thì lượt ghi vào IndexedDB sẽ không thành công. Trình xử lý onabort() của giao dịch sẽ được gọi, truyền một sự kiện. Sự kiện này sẽ bao gồm một DOMException trong thuộc tính lỗi. Việc kiểm tra lỗi name sẽ trả về QuotaExceededError.

const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
  const error = event.target.error; // DOMException
  if (error.name == 'QuotaExceededError') {
    // Fallback code goes here
  }
};

API bộ nhớ đệm

Nếu nguồn gốc đã vượt quá hạn mức, thì nỗ lực ghi vào API Bộ nhớ đệm sẽ bị từ chối bằng QuotaExceededError DOMException.

try {
  const cache = await caches.open('my-cache');
  await cache.add(new Request('/sample1.jpg'));
} catch (err) {
  if (error.name === 'QuotaExceededError') {
    // Fallback code goes here
  }
}

Quy trình chuyển quyền sở hữu diễn ra như thế nào?

Bộ nhớ web được chia thành hai bộ chứa: "Nỗ lực tốt nhất" và "Liên tục". Nỗ lực tối đa có nghĩa là trình duyệt có thể xoá bộ nhớ mà không làm gián đoạn người dùng, nhưng sẽ kém bền vững hơn đối với dữ liệu dài hạn hoặc quan trọng. Bộ nhớ liên tục sẽ không tự động bị xoá khi sắp hết bộ nhớ. Người dùng cần xoá bộ nhớ này theo cách thủ công (thông qua các chế độ cài đặt của trình duyệt).

Theo mặc định, dữ liệu của trang web (bao gồm IndexedDB, Cache API, v.v.) thuộc danh mục khả năng tối đa, nghĩa là trừ phi trang web yêu cầu bộ nhớ liên tục, trình duyệt có thể tuỳ ý loại bỏ dữ liệu trang web, chẳng hạn như khi thiết bị sắp hết bộ nhớ.

Chính sách về việc di chuyển để cố gắng hết sức có thể là:

  • Các trình duyệt dựa trên Chromium sẽ bắt đầu loại bỏ dữ liệu khi trình duyệt hết dung lượng, xoá tất cả dữ liệu trang web khỏi nguồn gốc được sử dụng gần đây nhất trước rồi mới đến lần tiếp theo cho đến khi trình duyệt không còn vượt quá giới hạn dung lượng.
  • Internet Explorer 10 trở lên sẽ không loại bỏ dữ liệu, nhưng sẽ ngăn nguồn gốc ghi thêm nữa.
  • Firefox sẽ bắt đầu loại bỏ dữ liệu khi dung lượng ổ đĩa còn trống đã đầy, trước tiên, hãy xoá tất cả dữ liệu trang web của nguồn gốc được sử dụng gần đây nhất, rồi sau đó xoá tất cả dữ liệu trang web cho đến khi trình duyệt không còn vượt quá giới hạn.
  • Trước đây, Safari không loại bỏ dữ liệu, nhưng gần đây đã triển khai giới hạn 7 ngày mới trên tất cả bộ nhớ có thể ghi (xem bên dưới).

Kể từ iOS và iPadOS 13.4 cũng như Safari 13.1 trên macOS, tất cả bộ nhớ có thể ghi tập lệnh sẽ có giới hạn 7 ngày, bao gồm cả IndexedDB, hoạt động đăng ký trình chạy dịch vụ và API Bộ nhớ đệm. Điều này có nghĩa là Safari sẽ loại bỏ tất cả nội dung khỏi bộ nhớ đệm sau 7 ngày sử dụng Safari nếu người dùng không tương tác với trang web. Chính sách loại bỏ này không áp dụng cho những PWA đã cài đặt đã được thêm vào màn hình chính. Xem Toàn bộ tính năng chặn cookie của bên thứ ba và các nội dung khác trên blog WebKit để biết đầy đủ chi tiết.

Thông tin bổ sung: Tại sao nên sử dụng trình bao bọc cho IndexedDB

IndexedDB là một API cấp thấp yêu cầu thiết lập đáng kể trước khi sử dụng, điều này có thể đặc biệt khó khăn khi lưu trữ dữ liệu đơn giản. Không giống như hầu hết các API dựa trên lời hứa hiện đại, API này dựa trên sự kiện. Các trình bao bọc lời hứa hẹn như idb cho IndexedDB ẩn một số tính năng mạnh mẽ, nhưng quan trọng hơn là ẩn máy móc phức tạp (ví dụ: các giao dịch, tạo phiên bản giản đồ) đi kèm với thư viện IndexedDB.

Kết luận

Không còn cái thời mà bộ nhớ hạn chế và nhắc người dùng lưu trữ nhiều dữ liệu hơn nữa. Các trang web có thể lưu trữ hiệu quả tất cả tài nguyên và dữ liệu cần thiết để chạy. Bằng cách sử dụng API StorageManager, bạn có thể xác định lượng dữ liệu và số tiền bạn đã sử dụng. Ngoài ra, với bộ nhớ ổn định, trừ phi người dùng xoá dữ liệu, bạn có thể bảo vệ dữ liệu đó khỏi bị loại bỏ.

Tài nguyên bổ sung

Cảm ơn bạn!

Đặc biệt cảm ơn Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink và Victor Costan vì đã xem bài viết này. Nhờ có Eiji Kitamura, Addy OSmani và Marc Cohen là những người đã viết nên các bài viết gốc để xây dựng nên bài viết này. Eiji đã viết một công cụ hữu ích có tên là Trình lạm dụng bộ nhớ trình duyệt. Công cụ này rất hữu ích trong việc xác thực hành vi hiện tại. Nó cho phép bạn lưu trữ nhiều dữ liệu nhất có thể và xem giới hạn bộ nhớ trên trình duyệt của bạn. Nhờ có Francois Beaufort đào sâu vào Safari để tìm ra giới hạn bộ nhớ của ứng dụng.

Hình ảnh chính là của Guillaume Bolduc trên Unsplash.