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

Bổ sung các kỹ thuật tải 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 nhiệm vụ trên trang web thường bao gồm một số bước. Ví dụ: để mua một sản phẩm trên trang web thương mại điện tử, bạn có thể phải 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 rồi hoàn tất thao tác bằng cách thanh toán.

Về mặt kỹ thuật, việc di chuyển qua các trang khác nhau có nghĩa là tạo một yêu cầu điều hướng. Theo nguyên tắc chung, bạn không nên sử dụng các tiêu đề Cache-Control tồn tại lâu dài để 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 yêu cầu này sẽ được đáp ứ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, là mới (ở mức hợp lý). Việc phải chống lại mạng mỗi khi người dùng chuyển đến một trang mới có nghĩa là mỗi thao tác điều hướng có thể bị chậm. Ít nhất, điều này có nghĩa là thao tác điều hướng sẽ không đáng tin cậy và nhanh.

Để tăng tốc các yêu cầu này, nếu có thể dự đoán hành động của người dùng, bạn có thể yêu cầu các trang và thành phần này trước và lưu các trang và thành phần đó vào 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á nhiều cách sử dụng trình chạy dịch vụ để bổ sung cho các kỹ thuật tải trước truyền thống.

Trường hợp phát hành công khai

MercadoLibre là trang web thương mại điện tử lớn nhất ở Mỹ Latinh. Để tăng tốc độ điều hướng, các thành phần này sẽ chèn linh động các thẻ <link rel="prefetch"> vào một số phần của luồng. Ví dụ: trong các trang thông tin, 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 thông tin thứ nhất và thứ hai của MercadoLibre và thẻ Tìm nạp trước đường liên kết kết nối cả hai trang.

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 (tuỳ thuộc vào việc tài nguyên có thể lưu vào bộ nhớ đệm hay không), trong một 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. Tài nguyên được giữ lại 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 tính năng lưu vào bộ nhớ đệm của worker dịch vụ có thể giúp bạn kéo dài thời gian hoạt động của các tài nguyên tải trước ngoài khoảng thời gian 5 phút.

Ví dụ: cổng thông tin thể thao Virgilio Sport của Ý sử dụng worker dịch vụ để tải trước các bài đăng phổ biến nhất trên trang chủ. Các ứng dụng này cũng sử dụng API Thông tin mạng để tránh tải trước cho những người dùng đang dùng kết nối 2G.

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

Do đó, sau hơn 3 tuần quan sát, Virgilio Sport đã chứng kiến thời gian tải để điều hướng đế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ải trước.

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

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

1. Lưu trước các trang tĩnh và tài nguyên phụ của trang

Lưu trước là khả năng của worker 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 vào bộ nhớ đệm trước được dùng để đạt được mục tiêu tương tự như tính năng tải trước: giúp thao tác điều hướng nhanh hơn.

Lưu các trang tĩnh vào bộ nhớ đệm trước

Đối với các trang được tạo tại thời điểm tạo bản dựng (ví dụ: about.html, contact.html) hoặc trong các trang web hoàn toàn tĩnh, bạn chỉ cần thêm tài liệu của trang web vào danh sách lưu vào bộ nhớ đệm trước để các tài liệu đó 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 các tài nguyên phụ của trang vào bộ nhớ đệm trước

Việc lưu trước các thành phầ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à một phương pháp hay nhất chung và có thể tăng cường thêm trong các trường hợp tải trước.

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

Cách triển khai:

  • 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 lưu vào bộ nhớ đệm trước trong worker dịch vụ:
workbox.precaching.precacheAndRoute([
  '/styles/product-page.ac29.css',
  // ... other entries ...
]);

2. Kéo dài thời gian hoạt động của tài nguyên tải trước

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

Worker dịch vụ cho phép bạn kéo dài thời gian hoạt động của các trang tải trước, đồng thời mang lại lợi ích bổ sung là cung cấp các tài nguyên đó để sử dụng khi không có mạng.

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

Cách triển khai:

  • 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 worker 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 ta đã chọn sử dụng chiến lược xác thực lại khi hết hạn. 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ó, nếu không thì đến từ mạng. Bộ nhớ đệm luôn được cập nhật bằng phản hồi mạng với mỗi yêu cầu thành công.

3. Uỷ quyền tải trước cho worker 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 ý tài nguyên được thiết kế để tải trước hiệu quả nhất có thể.

Tuy nhiên, trong một số trường hợp, bạn nên uỷ quyền hoàn toàn nhiệm vụ này cho worker dịch vụ. Ví dụ: để tải trước một số sản phẩm đầu tiên trong trang thông tin sản phẩm do phía máy khách hiển thị, bạn có thể cần 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 API. Điều 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 giữa trang và worker" để uỷ quyền hoàn toàn tác vụ tìm nạp trước cho worker. 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 của một trang giao tiếp hai chiều với trình chạy dịch vụ.

Gói Workbox Window đơ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 về lệnh gọi cơ bản đang được thực hiện.

Bạn có thể triển khai tính năng 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 trình này loại thông báo và danh sách URL để tải 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ụ: triển khai trình xử lý thông báo để đưa ra yêu cầu fetch() cho mỗi URL cần tải trước:
addEventListener('message', (event) => {
  if (event.data.type === 'PREFETCH_URLS') {
    // Fetch URLs and store them in the cache
  }
});