Hướng dẫn lưu vào bộ nhớ đệm bắt buộc

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

Một số trang web có thể cần giao tiếp với trình chạy dịch vụ mà không cần biết về kết quả. Dưới đây là một số ví dụ:

  • Một trang sẽ gửi cho trình chạy dịch vụ danh sách URL đến tìm nạp trước, để khi người dùng nhấp vào liên kết tài liệu hoặc các tài nguyên phụ của trang đã có sẵn trong bộ nhớ đệm, giúp điều hướng nhanh hơn nhiều.
  • Trang này yêu cầu trình chạy dịch vụ truy xuất và lưu một tập hợp các bài viết hàng đầu vào bộ nhớ đệm để có các bài viết này khả dụng cho mục đích ngoại tuyến.

Việc giao những loại công việc không quan trọng này cho trình chạy dịch vụ có lợi ích là giải phóng luồng chính để xử lý tốt hơn các tác vụ cấp bách hơn, chẳng hạn như phản hồi tương tác của người dùng.

Sơ đồ một trang yêu cầu tài nguyên để lưu vào bộ nhớ đệm cho một trình chạy dịch vụ.

Trong hướng dẫn này, chúng ta sẽ khám phá cách triển khai kỹ thuật giao tiếp một chiều từ trang để trình chạy dịch vụ bằng cách sử dụng API trình duyệt chuẩn và thư viện Workbox. Chúng tôi sẽ gọi những loại này là việc lưu bắt buộc vào bộ nhớ đệm trong các trường hợp sử dụng.

Trường hợp sản xuất

1-800-flowers.com đã triển khai việc lưu bắt buộc vào bộ nhớ đệm (tìm nạp trước) với các trình chạy dịch vụ thông qua postMessage() để tìm nạp trước các mặt hàng hàng đầu trong trang danh mục để tăng tốc độ di chuyển sau đó đến trang chi tiết sản phẩm.

Biểu trưng của 1-800 Hoa.

Chúng sử dụng phương pháp kết hợp để quyết định các mục cần tìm nạp trước:

  • Tại thời điểm tải trang, chúng yêu cầu worker phục vụ truy xuất dữ liệu JSON cho 9 mục hàng đầu, và thêm các đối tượng phản hồi kết quả vào bộ nhớ đệm.
  • Đối với các mục còn lại, họ nghe mouseover để khi một khi người dùng di chuyển con trỏ lên trên một mục, họ có thể kích hoạt tìm nạp tài nguyên theo "yêu cầu".

Các API này sử dụng API Bộ nhớ đệm để lưu trữ JSON phản hồi:

Biểu trưng của 1-800 Hoa.
Tìm nạp trước dữ liệu sản phẩm JSON từ các trang thông tin sản phẩm trong 1-800flowers.com.

Khi người dùng nhấp vào một mục, dữ liệu JSON liên kết với mục đó có thể được lấy từ bộ nhớ đệm. mà không cần kết nối mạng, giúp điều hướng nhanh hơn.

Sử dụng Workbox

Hộp làm việc giúp bạn dễ dàng gửi thư đến một trình chạy dịch vụ, thông qua gói workbox-window, một tập hợp các mô-đun nhằm chạy trong ngữ cảnh cửa sổ. Các gói này bổ sung cho các gói Workbox khác chạy trong trình chạy dịch vụ.

Để giao tiếp trang với trình chạy dịch vụ, trước tiên hãy lấy tham chiếu đối tượng Hộp công việc đến đối tượng trình chạy dịch vụ đã đăng ký:

const wb = new Workbox('/sw.js');
wb.register();

Sau đó, bạn có thể trực tiếp gửi thư một cách rõ ràng mà không cần lo lắng đăng ký, kiểm tra kích hoạt hoặc suy nghĩ về API liên lạc cơ bản:

wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });

Trình chạy dịch vụ triển khai trình xử lý message để hãy nghe những tin nhắn này. Lớp này có thể tuỳ ý trả về phản hồi, mặc dù trong những trường hợp như thế này, không cần thiết:

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PREFETCH') {
    // do something
  }
});

Sử dụng API trình duyệt

Nếu thư viện Workbox không đủ cho nhu cầu của bạn, sau đây là cách bạn có thể triển khai cửa sổ để dịch vụ giao tiếp của worker, bằng API trình duyệt.

API postMessage có thể được dùng để thiết lập cơ chế giao tiếp một chiều từ trang đến trình chạy dịch vụ.

Trang gọi postMessage() trên giao diện trình chạy dịch vụ:

navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
  payload: 'some data to perform the task',
});

Trình chạy dịch vụ triển khai trình xử lý message để hãy nghe những tin nhắn này.

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === MSG_ID) {
    // do something
  }
});

Thuộc tính {type : 'MSG_ID'} là không bắt buộc, nhưng đó là một cách để cho phép trang gửi các loại lệnh khác nhau tới trình chạy dịch vụ (nghĩa là "tìm nạp trước" và "để xoá lưu trữ"). Service worker có thể phân nhánh thành các đường dẫn thực thi khác nhau dựa trên cờ này.

Nếu thao tác thành công, người dùng sẽ có thể nhận được các lợi ích từ thao tác đó. Tuy nhiên, nếu không, thao tác này sẽ không làm thay đổi luồng người dùng chính. Ví dụ: khi 1-800-flowers.com cố gắng lưu bộ nhớ đệm trước, trang không cần biết liệu trình chạy dịch vụ có thành công hay không. Nếu có thì người dùng sẽ điều hướng nhanh hơn. Nếu không, trang vẫn cần điều hướng đến trang mới. Quá trình này sẽ mất nhiều thời gian hơn một chút.

Ví dụ đơn giản về tìm nạp trước

Một trong những ứng dụng phổ biến nhất của việc lưu bắt buộc vào bộ nhớ đệmtìm nạp trước, nghĩa là tìm nạp cho một URL nhất định, trước khi người dùng chuyển sang URL đó, nhằm tăng tốc độ điều hướng.

Có nhiều cách để triển khai phương thức tìm nạp trước trên trang web:

Đối với các trường hợp tìm nạp trước tương đối đơn giản, chẳng hạn như tìm nạp trước tài liệu hoặc các thành phần cụ thể (JS, CSS, v.v.), thì những kỹ thuật đó là phương pháp tốt nhất.

Nếu cần thêm logic, chẳng hạn như phân tích cú pháp tài nguyên tìm nạp trước (tệp hoặc trang JSON) trong để tìm nạp URL nội bộ, tốt hơn là bạn nên uỷ quyền toàn bộ tác vụ này cho trình chạy dịch vụ.

Việc uỷ quyền các loại thao tác này cho trình chạy dịch vụ có các ưu điểm sau:

  • Giảm bớt công việc tìm nạp và xử lý sau tìm nạp (sẽ được giới thiệu sau) sau) sang chuỗi phụ. Thao tác này giải phóng luồng chính để xử lý các vấn đề quan trọng hơn như phản hồi tương tác của người dùng.
  • Cho phép nhiều ứng dụng khách (ví dụ: các thẻ) sử dụng lại một chức năng phổ biến và thậm chí gọi hàm dịch vụ đồng thời mà không chặn luồng chính.

Tìm nạp trước trang chi tiết sản phẩm

Lần đầu sử dụng postMessage() trên giao diện của trình chạy dịch vụ và chuyển một mảng URL vào bộ nhớ đệm:

navigator.serviceWorker.controller.postMessage({
  type: 'PREFETCH',
  payload: {
    urls: [
      'www.exmaple.com/apis/data_1.json',
      'www.exmaple.com/apis/data_2.json',
    ],
  },
});

Trong trình chạy dịch vụ, hãy triển khai trình xử lý message để chặn và xử lý thông báo được gửi bởi bất kỳ thẻ đang hoạt động nào:

addEventListener('message', (event) => {
  let data = event.data;
  if (data && data.type === 'PREFETCH') {
    let urls = data.payload.urls;
    for (let i in urls) {
      fetchAsync(urls[i]);
    }
  }
});

Trong mã trước, chúng tôi đã giới thiệu một hàm trợ giúp nhỏ có tên là fetchAsync() để lặp lại một mảng URL và đưa ra yêu cầu tìm nạp cho từng URL đó:

async function fetchAsync(url) {
  // await response of fetch call
  let prefetched = await fetch(url);
  // (optionally) cache resources in the service worker storage
}

Khi nhận được phản hồi, bạn có thể dựa vào tiêu đề lưu vào bộ nhớ đệm của tài nguyên. Trong nhiều trường hợp mặc dù, giống như trong trang chi tiết sản phẩm, tài nguyên không được lưu vào bộ nhớ đệm (có nghĩa là chúng có Tiêu đề Cache-control trong số no-cache). Trong những trường hợp như vậy, bạn có thể ghi đè hành vi này bằng cách lưu trữ tài nguyên đã tìm nạp trong bộ nhớ đệm của trình chạy dịch vụ. Điều này có thêm một lợi ích là cho phép được phân phát trong môi trường ngoại tuyến.

Ngoài dữ liệu JSON

Sau khi dữ liệu JSON được tìm nạp từ điểm cuối của máy chủ, dữ liệu này thường chứa các URL khác cũng đáng tìm nạp trước, chẳng hạn như hình ảnh hoặc dữ liệu thiết bị đầu cuối khác được liên kết với cấp đầu tiên này .

Giả sử trong ví dụ này, dữ liệu JSON được trả về là thông tin của một trang web mua sắm tạp hoá:

{
  "productName": "banana",
  "productPic": "https://cdn.example.com/product_images/banana.jpeg",
  "unitPrice": "1.99"
 }

Sửa đổi mã fetchAsync() để lặp lại danh sách sản phẩm và lưu hình ảnh chính vào bộ nhớ đệm từng yếu tố trong số đó:

async function fetchAsync(url, postProcess) {
  // await response of fetch call
  let prefetched = await fetch(url);

  //(optionally) cache resource in the service worker cache

  // carry out the post fetch process if supplied
  if (postProcess) {
    await postProcess(prefetched);
  }
}

async function postProcess(prefetched) {
  let productJson = await prefetched.json();
  if (productJson && productJson.product_pic) {
    fetchAsync(productJson.product_pic);
  }
}

Bạn có thể thêm một số cách xử lý ngoại lệ liên quan đến mã này trong các tình huống như lỗi 404. Tuy nhiên, ưu điểm của việc sử dụng trình chạy dịch vụ để tìm nạp trước là nó có thể không thành công nếu không có nhiều kết quả đối với trang và chuỗi chính. Bạn cũng có thể có logic chi tiết hơn trong xử lý hậu kỳ nội dung được tìm nạp trước, giúp nội dung linh hoạt hơn và tách biệt với dữ liệu xử lý. Không có giới hạn.

Kết luận

Trong bài viết này, chúng tôi đã đề cập đến một trường hợp sử dụng phổ biến là giao tiếp một chiều giữa trang và dịch vụ worker: lưu vào bộ nhớ đệm bắt buộc. Các ví dụ được thảo luận chỉ nhằm mục đích minh hoạ một cách bằng cách dùng mẫu này và phương pháp tương tự cũng có thể được áp dụng cho các trường hợp sử dụng khác, ví dụ: lưu các bài viết hàng đầu theo yêu cầu vào bộ nhớ đệm để xem khi không có mạng, đánh dấu trang và các tính năng khác.

Để biết thêm các mẫu giao tiếp khác của trình chạy trang và trình chạy dịch vụ, hãy xem:

  • Cập nhật thông tin truyền phát: Gọi trang từ trình chạy dịch vụ để thông báo về các bản cập nhật quan trọng (ví dụ: có phiên bản mới của ứng dụng web).
  • Giao tiếp hai chiều: Uỷ quyền công việc cho một nhân viên dịch vụ (ví dụ: tải xuống rất nhiều) và thông báo tiến trình trên trang.