Trải nghiệm chỉ đường tức thì

Bổ sung các kỹ thuật tìm nạp trước truyền thống bằng trình chạy dịch vụ.

Demián Renzulli
Demián Renzulli
Gilberto Cocchi
Gilberto Cocchi,

Việc thực hiện một tác vụ trên trang web thường bao gồm nhiều bước. Ví dụ: Khi mua sản phẩm trên trang web thương mại điện tử, bạn có thể tìm kiếm sản phẩm, chọn một mặt hàng trong danh sách kết quả, thêm mặt hàng vào giỏ hàng và hoàn tất hoạt động bằng cách thanh toán.

Trong thuật ngữ kỹ thuật, việc di chuyển qua nhiều trang có nghĩa là thực hiện một yêu cầu điều hướng. Theo quy tắc chung, bạn không muốn sử dụng các tiêu đề Cache-Control dài hạn để lưu phản hồi HTML vào bộ nhớ đệm cho một yêu cầu điều hướng. Thông thường, các mã này phải được đáp ứng thông qua mạng bằng Cache-Control: no-cache để đảm bảo rằng HTML cùng với chuỗi yêu cầu mạng tiếp theo luôn mới (hợp lý). Rất tiếc, việc phải truy cập vào mạng mỗi khi người dùng điều hướng đến một trang mới có nghĩa là mỗi lần điều hướng có thể chậm — ít nhất thì điều đó có nghĩa là tốc độ nhanh đáng tin cậy.

Để đẩy nhanh tốc độ xử lý các yêu cầu này, nếu có thể dự đoán hành động của người dùng, thì bạn có thể yêu cầu trước các trang và thành phần này, rồi lưu chúng trong bộ nhớ đệm trong một khoảng thời gian ngắn cho đến khi người dùng nhấp vào các đường liên kết này. Kỹ thuật này được gọi là tìm nạp trước và thường được triển khai bằng cách thêm thẻ <link rel="prefetch"> vào các trang, cho biết tài nguyên cần tìm nạp trước.

Trong hướng dẫn này, chúng ta sẽ khám phá những cách khác nhau mà service worker có thể được sử dụng để bổ sung cho kỹ thuật tìm nạp trước truyền thống.

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

MercadoLibre là trang web thương mại điện tử lớn nhất tại Mỹ Latinh. Để tăng tốc độ điều hướng, các thẻ này sẽ tự động chèn thẻ <link rel="prefetch"> vào một số phần của quy trình. Ví dụ: trong các trang danh sách, các trang này sẽ tìm nạp trang kết quả tiếp theo ngay khi người dùng cuộn xuống cuối trang thông tin:

Ảnh chụp màn hình trang danh sách 1 và 2 của MercadoLibre và một thẻ Tìm nạp trước liên kết kết nối cả hai.

Các tệp được tìm nạp trước được yêu cầu ở mức độ ưu tiên "Thấp nhất" và được lưu trữ trong bộ nhớ đệm HTTP hoặc bộ nhớ đệm bộ nhớ (tuỳ thuộc vào việc tài nguyên có thể lưu vào bộ nhớ đệm hay không), trong khoảng thời gian khác nhau tuỳ theo trình duyệt. Ví dụ: kể từ Chrome 85, giá trị này là 5 phút. Các tài nguyên được lưu giữ trong 5 phút, sau đó các quy tắc Cache-Control thông thường cho tài nguyên sẽ được áp dụng.

Việc sử dụng chức năng lưu vào bộ nhớ đệm của trình chạy dịch vụ có thể giúp bạn kéo dài vòng đời của tài nguyên tìm nạp trước ngoài khoảng thời gian 5 phút.

Ví dụ: cổng thông tin thể thao Ý Virgilio Sport sử dụng trình chạy dịch vụ để tìm nạp trước các bài đăng phổ biến nhất trên trang chủ của họ. Các thiết bị này cũng sử dụng Network Information API (API Thông tin mạng) để tránh việc tìm nạp trước cho người dùng đang kết nối 2G.

Biểu trưng của Virgilio Sport.

Kết quả là trong hơn 3 tuần quan sát, Virgilio Sport nhận thấy thời gian tải các bài viết đến các bài viết đã cải thiện 78% và số lượt hiển thị bài viết tăng 45%.

Ảnh chụp màn hình trang chủ và trang bài viết của Virgilio Sport, với các chỉ số tác động sau khi tìm nạp trước.

Triển khai tính năng lưu trước vào bộ nhớ đệm bằng Workbox

Ở phần sau, chúng ta sẽ sử dụng Workbox để minh hoạ cách triển khai nhiều kỹ thuật lưu vào bộ nhớ đệm trong trình chạy dịch vụ. Kỹ thuật này có thể dùng để bổ sung cho <link rel="prefetch"> hay thậm chí là thay thế cho <link rel="prefetch"> bằng cách uỷ quyền hoàn toàn tác vụ này cho trình chạy dịch vụ.

1. Lưu trước các trang tĩnh và tài nguyên con của trang vào bộ nhớ đệm

Lưu trước vào bộ nhớ đệm là khả năng của trình chạy dịch vụ lưu tệp vào bộ nhớ đệm trong khi cài đặt.

Trong các trường hợp sau, tính năng lưu trước được dùng để đạt được mục tiêu tương tự như quá trình tìm nạp trước: giúp quá trình điều hướng nhanh hơn.

Lưu trước các trang tĩnh

Đối với các trang được tạo trong thời gian xây dựng (ví dụ: about.html, contact.html) hoặc trong các trang web hoàn toàn tĩnh, người dùng chỉ cần thêm tài liệu của trang web vào danh sách bộ nhớ đệm để các tài liệu này luôn có sẵn trong bộ nhớ đệm mỗi khi người dùng truy cập:

workbox.precaching.precacheAndRoute([
  {url: '/about.html', revision: 'abcd1234'},
  // ... other entries ...
]);

Lưu trước tài nguyên con của trang

Việc lưu trước các tài sản tĩnh mà các phần khác nhau của trang web có thể sử dụng (ví dụ: JavaScript, CSS, v.v.) là phương pháp chung hay nhất và có thể làm tăng thêm các trường hợp tìm nạp trước.

Để tăng tốc độ di chuyển trên một trang web thương mại điện tử, bạn có thể sử dụng thẻ <link rel="prefetch"> trong các trang thông tin để tìm nạp trước trang chi tiết sản phẩm cho một vài sản phẩm đầu tiên của một trang thông tin. Nếu bạn đã lưu trước các tài nguyên phụ của trang sản phẩm vào bộ nhớ đệm, thao tác này có thể giúp điều hướng nhanh hơn nữa.

Cách triển khai việc này:

  • Thêm thẻ <link rel="prefetch"> vào trang:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Thêm tài nguyên phụ của trang vào danh sách bộ nhớ đệm trước trong trình chạy dịch vụ:
workbox.precaching.precacheAndRoute([
  '/styles/product-page.ac29.css',
  // ... other entries ...
]);

2. Kéo dài thời gian tồn tại của tài nguyên tìm nạp trước

Như đã đề cập trước đó, <link rel="prefetch"> tìm nạp và lưu giữ các tài nguyên trong bộ nhớ đệm HTTP trong một khoảng thời gian giới hạn, sau đó các quy tắc Cache-Control cho một tài nguyên sẽ được áp dụng. Kể từ Chrome 85, giá trị này là 5 phút.

Trình chạy dịch vụ cho phép bạn kéo dài thời gian tồn tại của các trang tìm nạp trước, đồng thời mang lại thêm lợi ích là cung cấp các tài nguyên đó để sử dụng ngoại tuyến.

Trong ví dụ trước, một phần tử có thể bổ sung cho <link rel="prefetch"> dùng để tìm nạp trước một trang sản phẩm bằng chiến lược lưu vào bộ nhớ đệm trong thời gian chạy của Hộp công việc.

Để triển khai việc này, hãy làm như sau:

  • Thêm thẻ <link rel="prefetch"> vào trang:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Triển khai chiến lược lưu vào bộ nhớ đệm trong thời gian chạy trong trình chạy dịch vụ cho các loại yêu cầu sau:
new workbox.strategies.StaleWhileRevalidate({
  cacheName: 'document-cache',
  plugins: [
    new workbox.expiration.Plugin({
      maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
    }),
  ],
});

Trong trường hợp này, chúng tôi chọn sử dụng chiến lược đã lỗi thời trong khi xác thực lại. Trong chiến lược này, các trang có thể được yêu cầu song song từ cả bộ nhớ đệm và mạng. Phản hồi đến từ bộ nhớ đệm (nếu có) hoặc từ mạng. Bộ nhớ đệm luôn cập nhật phản hồi mạng với mỗi yêu cầu thành công.

3. Ủy quyền tìm nạp trước cho trình chạy dịch vụ

Trong hầu hết các trường hợp, phương pháp tốt nhất là sử dụng <link rel="prefetch">. Thẻ này là một gợi ý về tài nguyên được thiết kế để giúp việc tìm nạp trước diễn ra hiệu quả nhất có thể.

Mặc dù vậy, trong một số trường hợp, bạn nên uỷ quyền toàn bộ tác vụ này cho trình chạy dịch vụ. Ví dụ: để tìm nạp trước một số sản phẩm đầu tiên trong trang thông tin sản phẩm được hiển thị phía máy khách, người dùng có thể cần phải chèn một số thẻ <link rel="prefetch"> một cách linh động vào trang, dựa trên phản hồi của API. Việc này có thể tạm thời làm tiêu tốn thời gian trên luồng chính của trang và khiến việc triển khai trở nên khó khăn hơn.

Trong những trường hợp như vậy, hãy sử dụng "chiến lược giao tiếp từ trang với trình chạy dịch vụ" để uỷ quyền tìm nạp trước hoàn toàn cho trình chạy dịch vụ. Bạn có thể thực hiện loại giao tiếp này bằng cách sử dụng worker.postMessage():

Biểu tượng về một trang đang giao tiếp hai chiều với nhân viên dịch vụ.

Gói Cửa sổ hộp công việc giúp đơn giản hoá loại hình giao tiếp này, tóm tắt nhiều thông tin chi tiết của lệnh gọi cơ bản đang được thực hiện.

Bạn có thể triển khai quá trình tìm nạp trước bằng Cửa sổ Workbox theo cách sau:

  • Trong trang: gọi trình chạy dịch vụ truyền cho loại thông báo đó và danh sách URL cần tìm nạp trước:
const wb = new Workbox('/sw.js');
wb.register();

const prefetchResponse = await wb.messageSW({type: 'PREFETCH_URLS', urls: […]});
  • Trong trình chạy dịch vụ: hãy triển khai một trình xử lý thông báo để đưa ra yêu cầu fetch() cho mỗi URL cần tìm nạp trước:
addEventListener('message', (event) => {
  if (event.data.type === 'PREFETCH_URLS') {
    // Fetch URLs and store them in the cache
  }
});