API Bộ nhớ đệm: Hướng dẫn nhanh

Tìm hiểu cách sử dụng API bộ nhớ đệm để cung cấp dữ liệu ứng dụng khi không có mạng.

Cache API là một hệ thống lưu trữ và truy xuất các yêu cầu mạng cũng như phản hồi tương ứng. Đây có thể là các yêu cầu và phản hồi thông thường được tạo trong quá trình chạy ứng dụng hoặc chỉ được tạo với mục đích lưu trữ dữ liệu để sử dụng sau này.

Cache API được tạo để cho phép worker dịch vụ lưu các yêu cầu mạng vào bộ nhớ đệm để có thể phản hồi nhanh, bất kể tốc độ mạng hoặc khả năng sử dụng. Tuy nhiên, API này cũng có thể được dùng làm cơ chế lưu trữ chung.

Cache API có trong tất cả trình duyệt hiện đại. API này được hiển thị thông qua thuộc tính caches toàn cục, vì vậy, bạn có thể kiểm tra sự hiện diện của API bằng một tính năng phát hiện đơn giản:

const cacheAvailable = 'caches' in self;

Hỗ trợ trình duyệt

  • Chrome: 40.
  • Edge: 16.
  • Firefox: 41.
  • Safari: 11.1.

Nguồn

Bạn có thể truy cập vào API bộ nhớ đệm từ một cửa sổ, iframe, worker hoặc trình chạy dịch vụ.

Nội dung có thể lưu trữ

Bộ nhớ đệm chỉ lưu trữ các cặp đối tượng RequestResponse, tương ứng với các yêu cầu và phản hồi HTTP. Tuy nhiên, các yêu cầu và phản hồi có thể chứa bất kỳ loại dữ liệu nào có thể được chuyển qua HTTP.

Có thể lưu trữ bao nhiêu?

Tóm lại, rất nhiều, ít nhất là vài trăm megabyte và có thể lên đến hàng trăm gigabyte trở lên. Việc triển khai trình duyệt có thể 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ị.

Tạo và mở bộ nhớ đệm

Để mở bộ nhớ đệm, hãy sử dụng phương thức caches.open(name), truyền tên của bộ nhớ đệm dưới dạng tham số duy nhất. Nếu bộ nhớ đệm được đặt tên không tồn tại, bộ nhớ đệm đó sẽ được tạo. Phương thức này trả về một Promise phân giải với đối tượng Cache.

const cache = await caches.open('my-cache');
// do something with cache...

Thêm vào bộ nhớ đệm

Có ba cách để thêm một mục vào bộ nhớ đệm – add, addAllput. Cả ba phương thức đều trả về Promise.

cache.add

Trước tiên, hãy xem cache.add(). Phương thức này nhận một tham số, Request hoặc URL (string). Phương thức này sẽ gửi yêu cầu đến mạng và lưu trữ phản hồi trong bộ nhớ đệm. Nếu quá trình tìm nạp không thành công hoặc nếu mã trạng thái của phản hồi không nằm trong phạm vi 200, thì sẽ không có gì được lưu trữ và Promise sẽ từ chối. Xin lưu ý rằng bạn không thể lưu trữ các yêu cầu qua nhiều nguồn gốc không ở chế độ CORS vì các yêu cầu này trả về một status của 0. Bạn chỉ có thể lưu trữ các yêu cầu đó bằng put.

// Retreive data.json from the server and store the response.
cache.add(new Request('/data.json'));

// Retreive data.json from the server and store the response.
cache.add('/data.json');

cache.addAll

Tiếp theo là cache.addAll(). Phương thức này hoạt động tương tự như add(), nhưng nhận một mảng các đối tượng Request hoặc URL (string). Cách này hoạt động tương tự như việc gọi cache.add cho từng yêu cầu riêng lẻ, ngoại trừ việc Promise sẽ từ chối nếu bất kỳ yêu cầu nào không được lưu vào bộ nhớ đệm.

const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);

Trong mỗi trường hợp này, mục nhập mới sẽ ghi đè mọi mục nhập hiện có trùng khớp. Phương thức này sử dụng các quy tắc so khớp giống như mô tả trong phần truy xuất.

cache.put

Cuối cùng, có cache.put() cho phép bạn lưu trữ phản hồi từ mạng hoặc tạo và lưu trữ Response của riêng bạn. Hàm này nhận hai tham số. Đối tượng đầu tiên có thể là đối tượng Request hoặc URL (string). Đối tượng thứ hai phải là Response, từ mạng hoặc do mã của bạn tạo ra.

// Retrieve data.json from the server and store the response.
cache.put('/data.json');

// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{"foo": "bar"}'));

// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');

Phương thức put() có nhiều quyền hơn add() hoặc addAll() và sẽ cho phép bạn lưu trữ các phản hồi không phải CORS hoặc các phản hồi khác mà mã trạng thái của phản hồi không nằm trong phạm vi 200. Lệnh này sẽ ghi đè mọi phản hồi trước đó cho cùng một yêu cầu.

Tạo đối tượng Yêu cầu

Tạo đối tượng Request bằng URL của nội dung đang được lưu trữ:

const request = new Request('/my-data-store/item-id');

Làm việc với các đối tượng Response

Hàm khởi tạo đối tượng Response chấp nhận nhiều loại dữ liệu, bao gồm các đối tượng Blob, ArrayBuffer, FormData và chuỗi.

const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');

Bạn có thể đặt loại MIME của Response bằng cách đặt tiêu đề thích hợp.

  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);

Nếu đã truy xuất Response và muốn truy cập vào nội dung của tệp này, bạn có thể sử dụng một số phương thức trợ giúp. Mỗi hàm trả về một Promise phân giải bằng một giá trị thuộc loại khác.

Phương thức Mô tả
arrayBuffer Trả về một ArrayBuffer chứa nội dung, được chuyển đổi tuần tự thành các byte.
blob Trả về một Blob. Nếu Response được tạo bằng Blob, thì Blob mới này sẽ có cùng loại. Nếu không, Content-Type của Response sẽ được sử dụng.
text Diễn giải các byte của nội dung dưới dạng chuỗi được mã hoá UTF-8.
json Diễn giải các byte của nội dung dưới dạng chuỗi được mã hoá UTF-8, sau đó cố gắng phân tích cú pháp chuỗi đó dưới dạng JSON. Trả về đối tượng thu được hoặc gửi một TypeError nếu không thể phân tích cú pháp chuỗi dưới dạng JSON.
formData Diễn giải các byte của nội dung dưới dạng một biểu mẫu HTML, được mã hoá dưới dạng multipart/form-data hoặc application/x-www-form-urlencoded. Trả về đối tượng FormData hoặc gửi TypeError nếu không thể phân tích cú pháp dữ liệu.
body Trả về một ReadableStream cho dữ liệu nội dung.

Ví dụ

const response = new Response('Hello world');
const buffer = await response.arrayBuffer();
console.log(new Uint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

Truy xuất từ bộ nhớ đệm

Để tìm một mục trong bộ nhớ đệm, bạn có thể sử dụng phương thức match.

const response = await cache.match(request);
console.log(request, response);

Nếu request là một chuỗi, trình duyệt sẽ chuyển đổi chuỗi đó thành Request bằng cách gọi new Request(request). Hàm này trả về một Promise phân giải thành Response nếu tìm thấy mục nhập trùng khớp hoặc undefined nếu không.

Để xác định xem hai Requests có khớp nhau hay không, trình duyệt không chỉ sử dụng URL. Hai yêu cầu được coi là khác nhau nếu có chuỗi truy vấn, tiêu đề Vary hoặc phương thức HTTP khác nhau (GET, POST, PUT, v.v.).

Bạn có thể bỏ qua một số hoặc tất cả những điều này bằng cách truyền đối tượng tuỳ chọn làm tham số thứ hai.

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const response = await cache.match(request, options);
// do something with the response

Nếu có nhiều yêu cầu được lưu vào bộ nhớ đệm khớp thì yêu cầu được tạo trước sẽ được trả về. Nếu muốn truy xuất tất cả câu trả lời trùng khớp, bạn có thể sử dụng cache.matchAll().

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const responses = await cache.matchAll(request, options);
console.log(`There are ${responses.length} matching responses.`);

Dưới dạng lối tắt, bạn có thể tìm kiếm trên tất cả bộ nhớ đệm cùng một lúc bằng cách sử dụng caches.match() thay vì gọi cache.match() cho mỗi bộ nhớ đệm.

Đang tìm kiếm

API bộ nhớ đệm không cung cấp cách tìm kiếm các yêu cầu hoặc phản hồi ngoại trừ các mục nhập khớp với đối tượng Response. Tuy nhiên, bạn có thể triển khai tính năng tìm kiếm của riêng mình bằng cách lọc hoặc tạo chỉ mục.

Lọc

Một cách để triển khai tính năng tìm kiếm của riêng bạn là lặp lại tất cả các mục nhập và lọc ra những mục nhập mà bạn muốn. Giả sử bạn muốn tìm tất cả các mục có URL kết thúc bằng .png.

async function findImages() {
  // Get a list of all of the caches for this origin
  const cacheNames = await caches.keys();
  const result = [];

  for (const name of cacheNames) {
    // Open the cache
    const cache = await caches.open(name);

    // Get a list of entries. Each item is a Request object
    for (const request of await cache.keys()) {
      // If the request URL matches, add the response to the result
      if (request.url.endsWith('.png')) {
        result.push(await cache.match(request));
      }
    }
  }

  return result;
}

Bằng cách này, bạn có thể sử dụng bất kỳ thuộc tính nào của đối tượng RequestResponse để lọc các mục nhập. Xin lưu ý rằng việc này sẽ diễn ra chậm nếu bạn tìm kiếm trên các tập dữ liệu lớn.

Tạo chỉ mục

Một cách khác để triển khai tính năng tìm kiếm của riêng bạn là duy trì một chỉ mục riêng biệt của các mục nhập có thể tìm kiếm và lưu trữ chỉ mục đó trong IndexedDB. Vì đây là loại thao tác mà IndexedDB được thiết kế để thực hiện, nên nó có hiệu suất tốt hơn nhiều với số lượng mục nhập lớn.

Nếu lưu trữ URL của Request cùng với các thuộc tính có thể tìm kiếm, thì bạn có thể dễ dàng truy xuất mục nhập bộ nhớ đệm chính xác sau khi tìm kiếm.

Xoá một mục

Cách xoá một mục khỏi bộ nhớ đệm:

cache.delete(request);

Trong đó, yêu cầu có thể là Request hoặc chuỗi URL. Phương thức này cũng lấy đối tượng tuỳ chọn giống như cache.match, cho phép bạn xoá nhiều cặp Request/Response cho cùng một URL.

cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});

Xoá bộ nhớ đệm

Để xoá bộ nhớ đệm, hãy gọi caches.delete(name). Hàm này trả về một Promise phân giải thành true nếu bộ nhớ đệm tồn tại và đã bị xoá, hoặc false nếu không.

Cảm ơn bạn!

Cảm ơn Mat Scales đã viết phiên bản gốc của bài viết này. Phiên bản này xuất hiện lần đầu trên WebFundamentals.