Ứng dụng nhiều trang nhanh hơn với luồng

Ngày nay, các trang web (hoặc ứng dụng web nếu bạn muốn) thường sử dụng một trong hai lược đồ điều hướng sau:

  • Theo mặc định, trình duyệt lược đồ điều hướng cung cấp thông tin: nghĩa là bạn nhập một URL vào thanh địa chỉ của trình duyệt và một yêu cầu điều hướng sẽ trả về một tài liệu dưới dạng phản hồi. Sau đó, bạn nhấp vào một đường liên kết để huỷ tải tài liệu hiện tại cho một tài liệu khác, ad infinitum.
  • Mẫu ứng dụng một trang, bao gồm yêu cầu điều hướng ban đầu để tải shell ứng dụng và dựa vào JavaScript để điền vào shell ứng dụng bằng mã đánh dấu do ứng dụng kết xuất với nội dung từ API phụ trợ cho mỗi "navigation".

Lợi ích của mỗi phương pháp đều được những người ủng hộ đề cao:

  • Giao thức điều hướng mà các trình duyệt cung cấp theo mặc định có khả năng phục hồi vì các tuyến không yêu cầu JavaScript truy cập được. Việc ứng dụng khách hiển thị mã đánh dấu bằng JavaScript cũng có thể là một quá trình tốn kém tiềm năng, nghĩa là các thiết bị cấp thấp hơn có thể gặp tình huống nội dung bị trễ do thiết bị bị chặn xử lý các tập lệnh cung cấp nội dung.
  • Mặt khác, Ứng dụng trang đơn (SPA) có thể cung cấp điều hướng nhanh hơn sau lần tải ban đầu. Thay vì phụ thuộc vào trình duyệt để tải một tài liệu hoàn toàn mới (và lặp lại điều này cho mỗi lần điều hướng), họ có thể cung cấp trải nghiệm nhanh hơn, "giống ứng dụng" hơn — ngay cả khi điều đó yêu cầu JavaScript hoạt động.

Trong bài đăng này, chúng ta sẽ nói về phương pháp thứ ba giúp cân bằng giữa hai cách tiếp cận được mô tả ở trên: dựa vào service worker để lưu trước các phần tử phổ biến của trang web – chẳng hạn như đánh dấu tiêu đề và chân trang – và sử dụng luồng để cung cấp phản hồi HTML cho ứng dụng khách nhanh nhất có thể, trong khi vẫn sử dụng lược đồ điều hướng mặc định của trình duyệt.

Tại sao bạn nên truyền phản hồi HTML trong một trình chạy dịch vụ?

Phát trực tuyến là tính năng mà trình duyệt web của bạn đã thực hiện khi đưa ra yêu cầu. Điều này cực kỳ quan trọng trong yêu cầu điều hướng, vì nó đảm bảo trình duyệt không bị chặn chờ toàn bộ phản hồi trước khi có thể bắt đầu phân tích cú pháp mã đánh dấu tài liệu và kết xuất trang.

Sơ đồ mô tả HTML không truyền trực tuyến so với HTML truyền trực tuyến. Trong trường hợp trước, toàn bộ tải trọng đánh dấu sẽ không được xử lý cho đến khi nhận được. Trong giai đoạn sau, mã đánh dấu được xử lý dần dần khi đến trong các đoạn từ mạng.

Đối với trình chạy dịch vụ, việc truyền trực tuyến hơi khác vì sử dụng API luồng JavaScript. Nhiệm vụ quan trọng nhất mà một trình chạy dịch vụ thực hiện là chặn và phản hồi các yêu cầu, bao gồm cả yêu cầu điều hướng.

Các yêu cầu này có thể tương tác với bộ nhớ đệm theo nhiều cách, tuy nhiên, một quy trình lưu vào bộ nhớ đệm phổ biến cho mục đánh dấu là ưu tiên sử dụng phản hồi từ mạng trước tiên, sau đó quay lại bộ nhớ đệm nếu có bản sao cũ hơn và cung cấp phản hồi dự phòng chung nếu có phản hồi hữu dụng không có trong bộ nhớ đệm.

Đây là một mẫu đánh dấu đã qua thử nghiệm theo thời gian để đánh dấu hoạt động tốt. Tuy nhiên, mặc dù giúp tăng cường độ tin cậy về mặt truy cập ngoại tuyến, nhưng mẫu này không cung cấp bất kỳ lợi thế vốn có về hiệu suất nào cho các yêu cầu điều hướng dựa vào chiến lược ưu tiên mạng hoặc chiến lược chỉ mạng. Đó là lúc chúng ta cần đến tính năng truyền trực tuyến. Chúng ta sẽ tìm hiểu cách sử dụng mô-đun workbox-streams dựa trên API của Luồng (Luồng) trong trình chạy dịch vụ Workbox để tăng tốc độ yêu cầu di chuyển trên trang web nhiều trang.

Chia nhỏ một trang web thông thường

Về cấu trúc, các trang web có xu hướng có các thành phần phổ biến trên mỗi trang. Cách sắp xếp thông thường của các phần tử trang thường có dạng như:

  • Tiêu đề.
  • Nội dung.
  • Chân trang.

Lấy web.dev làm ví dụ, bảng phân tích các thành phần phổ biến sẽ có dạng như sau:

Bảng chi tiết về các thành phần thường gặp trên trang web web.dev. Các khu vực chung được mô tả được đánh dấu là "tiêu đề", "nội dung" và "chân trang".

Mục tiêu phía sau việc xác định các phần của một trang là chúng tôi xác định những nội dung có thể được lưu trước vào bộ nhớ đệm và truy xuất mà không cần đến mạng — cụ thể là mã đánh dấu đầu trang và chân trang thường gặp trên tất cả các trang — và phần của trang mà chúng ta sẽ luôn truy cập vào mạng trước tiên — nội dung trong trường hợp này.

Khi biết cách phân đoạn các phần của một trang và xác định các phần tử phổ biến, chúng ta có thể viết một trình chạy dịch vụ luôn truy xuất ngay mã đánh dấu đầu trang và chân trang từ bộ nhớ đệm trong khi chỉ yêu cầu nội dung từ mạng.

Sau đó, bằng cách sử dụng API luồng qua workbox-streams, chúng ta có thể ghép tất cả các phần này lại với nhau và phản hồi các yêu cầu điều hướng ngay lập tức trong khi vẫn yêu cầu số lượng mã đánh dấu tối thiểu cần thiết từ mạng.

Xây dựng trình chạy dịch vụ truyền trực tuyến

Có rất nhiều bộ phận liên quan đến việc truyền trực tuyến một phần nội dung trong một trình chạy dịch vụ. Tuy nhiên, chúng tôi sẽ tìm hiểu từng bước của quy trình này một cách chi tiết, bắt đầu bằng cách cấu trúc trang web của bạn.

Phân đoạn trang web của bạn thành các phần

Trước khi có thể bắt đầu viết một worker dịch vụ truyền trực tuyến, bạn cần thực hiện 3 việc sau:

  1. Tạo một tệp chỉ chứa mã đánh dấu tiêu đề trang web của bạn.
  2. Tạo một tệp chỉ chứa mã đánh dấu chân trang của trang web.
  3. Kéo nội dung chính của từng trang vào một tệp riêng hoặc thiết lập phần phụ trợ để chỉ phân phát nội dung trang theo điều kiện dựa trên tiêu đề của yêu cầu HTTP.

Như bạn có thể mong đợi, bước cuối cùng là bước khó nhất, đặc biệt là khi trang web của bạn tĩnh. Trong trường hợp đó, bạn sẽ cần tạo hai phiên bản của mỗi trang: một phiên bản sẽ chứa mã đánh dấu trang đầy đủ trong khi phiên bản còn lại chỉ chứa nội dung.

Soạn một trình chạy dịch vụ truyền trực tuyến

Nếu chưa cài đặt mô-đun workbox-streams, bạn cần cài đặt mô-đun này cùng với bất kỳ mô-đun Workbox nào bạn hiện đã cài đặt. Trong ví dụ cụ thể này, có liên quan đến các gói sau:

npm i workbox-navigation-preload workbox-strategies workbox-routing workbox-precaching workbox-streams --save

Từ đây, bước tiếp theo là tạo trình chạy dịch vụ mới và lưu trước các phần của đầu trang và chân trang vào bộ nhớ đệm.

Đang lưu trước các phần vào bộ nhớ đệm

Điều đầu tiên bạn cần làm là tạo một trình chạy dịch vụ trong thư mục gốc của dự án có tên sw.js (hoặc bất kỳ tên tệp nào bạn muốn). Trong đó, bạn sẽ bắt đầu với những điều sau:

// sw.js
import * as navigationPreload from 'workbox-navigation-preload';
import {NetworkFirst} from 'workbox-strategies';
import {registerRoute} from 'workbox-routing';
import {matchPrecache, precacheAndRoute} from 'workbox-precaching';
import {strategy as composeStrategies} from 'workbox-streams';

// Enable navigation preload for supporting browsers
navigationPreload.enable();

// Precache partials and some static assets
// using the InjectManifest method.
precacheAndRoute([
  // The header partial:
  {
    url: '/partial-header.php',
    revision: __PARTIAL_HEADER_HASH__
  },
  // The footer partial:
  {
    url: '/partial-footer.php',
    revision: __PARTIAL_FOOTER_HASH__
  },
  // The offline fallback:
  {
    url: '/offline.php',
    revision: __OFFLINE_FALLBACK_HASH__
  },
  ...self.__WB_MANIFEST
]);

// To be continued...

Mã này có một vài chức năng:

  1. Bật tính năng tải trước điều hướng cho các trình duyệt hỗ trợ tính năng này.
  2. Lưu sẵn mã đánh dấu đầu trang và chân trang vào bộ nhớ đệm. Điều này có nghĩa là mã đánh dấu đầu trang và chân trang cho mỗi trang sẽ được truy xuất ngay lập tức vì nó sẽ không bị mạng chặn.
  3. Lưu trước vào bộ nhớ đệm các thành phần tĩnh trong phần giữ chỗ __WB_MANIFEST sử dụng phương thức injectManifest.

Câu trả lời theo thời gian thực

Việc để trình chạy dịch vụ của bạn truyền các phản hồi nối nhau là phần lớn nhất trong toàn bộ nỗ lực này. Mặc dù vậy, Workbox và workbox-streams của nó giúp việc này trở nên ngắn gọn hơn nhiều so với khi bạn phải tự mình làm tất cả những việc này:

// sw.js
import * as navigationPreload from 'workbox-navigation-preload';
import {NetworkFirst} from 'workbox-strategies';
import {registerRoute} from 'workbox-routing';
import {matchPrecache, precacheAndRoute} from 'workbox-precaching';
import {strategy as composeStrategies} from 'workbox-streams';

// ...
// Prior navigation preload and precaching code omitted...
// ...

// The strategy for retrieving content partials from the network:
const contentStrategy = new NetworkFirst({
  cacheName: 'content',
  plugins: [
    {
      // NOTE: This callback will never be run if navigation
      // preload is not supported, because the navigation
      // request is dispatched while the service worker is
      // booting up. This callback will only run if navigation
      // preload is _not_ supported.
      requestWillFetch: ({request}) => {
        const headers = new Headers();

        // If the browser doesn't support navigation preload, we need to
        // send a custom `X-Content-Mode` header for the back end to use
        // instead of the `Service-Worker-Navigation-Preload` header.
        headers.append('X-Content-Mode', 'partial');

        // Send the request with the new headers.
        // Note: if you're using a static site generator to generate
        // both full pages and content partials rather than a back end
        // (as this example assumes), you'll need to point to a new URL.
        return new Request(request.url, {
          method: 'GET',
          headers
        });
      },
      // What to do if the request fails.
      handlerDidError: async ({request}) => {
        return await matchPrecache('/offline.php');
      }
    }
  ]
});

// Concatenates precached partials with the content partial
// obtained from the network (or its fallback response).
const navigationHandler = composeStrategies([
  // Get the precached header markup.
  () => matchPrecache('/partial-header.php'),
  // Get the content partial from the network.
  ({event}) => contentStrategy.handle(event),
  // Get the precached footer markup.
  () => matchPrecache('/partial-footer.php')
]);

// Register the streaming route for all navigation requests.
registerRoute(({request}) => request.mode === 'navigate', navigationHandler);

// Your service worker can end here, or you can add more
// logic to suit your needs, such as runtime caching, etc.

Mã này bao gồm 3 phần chính đáp ứng các yêu cầu sau:

  1. Chiến lược NetworkFirst dùng để xử lý yêu cầu về một phần nội dung. Khi sử dụng chiến lược này, tên bộ nhớ đệm tuỳ chỉnh của content được chỉ định để chứa các phần nội dung, cũng như một trình bổ trợ tuỳ chỉnh giúp xử lý việc thiết lập tiêu đề yêu cầu X-Content-Mode cho các trình duyệt không hỗ trợ tải trước tính năng điều hướng (và do đó không gửi tiêu đề Service-Worker-Navigation-Preload). Trình bổ trợ này cũng chỉ ra liệu nên gửi một phần phiên bản nội dung đã lưu vào bộ nhớ đệm gần đây nhất hay gửi một trang dự phòng ngoại tuyến trong trường hợp không lưu trữ phiên bản nào đã lưu vào bộ nhớ đệm cho yêu cầu hiện tại.
  2. Phương thức strategy trong workbox-streams (có bí danh là composeStrategies ở đây) được dùng để nối một phần phần đầu trang và chân trang được lưu trước vào bộ nhớ đệm cùng với một phần nội dung được yêu cầu qua mạng.
  3. Toàn bộ lược đồ này được thiết lập thông qua registerRoute cho các yêu cầu điều hướng.

Với logic này, chúng ta đã thiết lập các câu trả lời theo thời gian thực. Tuy nhiên, có thể bạn sẽ cần thực hiện một số thao tác trên máy chủ phụ trợ để đảm bảo rằng nội dung từ mạng là một trang một phần mà bạn có thể hợp nhất với các trang được lưu trước trong bộ nhớ đệm.

Nếu trang web của bạn có phần phụ trợ

Bạn sẽ nhớ lại rằng khi bật tính năng tải trước điều hướng, trình duyệt sẽ gửi tiêu đề Service-Worker-Navigation-Preload có giá trị true. Tuy nhiên, trong mã mẫu ở trên, chúng ta đã gửi tiêu đề tùy chỉnh của X-Content-Mode trong việc tải trước điều hướng sự kiện không được hỗ trợ trong trình duyệt. Trong chương trình phụ trợ, bạn nên thay đổi nội dung phản hồi dựa trên sự hiện diện của các tiêu đề này. Trong một chương trình phụ trợ PHP, trang đó có thể có dạng như sau cho một trang nhất định:

<?php
// Check if we need to render a content partial
$navPreloadSupported = isset($_SERVER['HTTP_SERVICE_WORKER_NAVIGATION_PRELOAD']) && $_SERVER['HTTP_SERVICE_WORKER_NAVIGATION_PRELOAD'] === 'true';
$partialContentMode = isset($_SERVER['HTTP_X_CONTENT_MODE']) && $_SERVER['HTTP_X_CONTENT_MODE'] === 'partial';
$isPartial = $navPreloadSupported || $partialContentMode;

// Figure out whether to render the header
if ($isPartial === false) {
  // Get the header include
  require_once($_SERVER['DOCUMENT_ROOT'] . '/includes/site-header.php');

  // Render the header
  siteHeader();
}

// Get the content include
require_once('./content.php');

// Render the content
content($isPartial);

// Figure out whether to render the footer
if ($isPartial === false) {
  // Get the footer include
  require_once($_SERVER['DOCUMENT_ROOT'] . '/includes/site-footer.php');

  // Render the footer
  siteFooter();
}
?>

Trong ví dụ trên, nội dung một phần được gọi dưới dạng các hàm. Hàm này lấy giá trị của $isPartial để thay đổi cách kết xuất các phần tử đó. Ví dụ: hàm kết xuất đồ hoạ content chỉ có thể bao gồm một số mã đánh dấu nhất định trong các điều kiện khi được truy xuất dưới dạng một phần — một nội dung sẽ sớm được đề cập.

Những yếu tố nên cân nhắc

Trước khi triển khai một trình chạy dịch vụ để truyền trực tuyến và ghép các phần với nhau, bạn phải cân nhắc một số điều. Mặc dù đúng là việc sử dụng trình chạy dịch vụ theo cách này về cơ bản không làm thay đổi hành vi điều hướng mặc định của trình duyệt, nhưng có một số việc mà bạn có thể sẽ cần phải giải quyết.

Cập nhật các phần tử trang khi điều hướng

Phần khó khăn nhất của phương pháp này là một số nội dung cần được cập nhật trên ứng dụng. Ví dụ: mã đánh dấu tiêu đề lưu trước có nghĩa là trang sẽ có cùng nội dung trong phần tử <title>. Thậm chí, việc quản lý trạng thái bật/tắt của các mục điều hướng cũng sẽ phải được cập nhật trong mỗi lần điều hướng. Những nội dung này và những nội dung khác có thể phải được cập nhật trên ứng dụng cho mỗi yêu cầu điều hướng.

Cách để giải quyết vấn đề này có thể là đặt một phần tử <script> cùng dòng vào phần nội dung của mạng đó để cập nhật một số điểm quan trọng:

<!-- The JSON below contains information about the current page. -->
<script id="page-data" type="application/json">'{"title":"Sand Wasp &mdash; World of Wasps","description":"Read all about the sand wasp in this tidy little post."}'</script>
<script>
  const pageData = JSON.parse(document.getElementById('page-data').textContent);

  // Update the page title
  document.title = pageData.title;
</script>
<article>
  <!-- Page content omitted... -->
</article>

Đây chỉ là một ví dụ về những việc bạn có thể phải làm nếu quyết định thiết lập trình chạy dịch vụ này. Ví dụ: đối với các ứng dụng phức tạp hơn có thông tin người dùng, bạn có thể phải lưu trữ các bit dữ liệu liên quan trong một cửa hàng web như localStorage và cập nhật trang từ đó.

Xử lý mạng chậm

Một hạn chế của việc truyền trực tuyến phản hồi bằng mã đánh dấu từ bộ nhớ đệm trước có thể xảy ra khi kết nối mạng chậm. Vấn đề là mã đánh dấu tiêu đề từ bộ nhớ đệm trước sẽ xuất hiện ngay lập tức, nhưng nội dung một phần từ mạng có thể mất khá nhiều thời gian để đến sau lần vẽ đầu tiên của mã đánh dấu tiêu đề.

Điều này có thể tạo ra trải nghiệm khó hiểu và nếu mạng rất chậm, thậm chí bạn có thể cảm thấy như trang bị hỏng và không hiển thị thêm nữa. Trong trường hợp như vậy, bạn có thể chọn đặt biểu tượng hoặc thông báo đang tải trong mã đánh dấu của một phần nội dung mà bạn có thể ẩn đi sau khi nội dung được tải.

Một cách để thực hiện việc này là thông qua CSS. Giả sử một phần tiêu đề kết thúc bằng phần tử <article> mở và bị trống cho đến khi phần nội dung đến để điền vào. Bạn có thể viết quy tắc CSS tương tự như sau:

article:empty::before {
  text-align: center;
  content: 'Loading...';
}

Thao tác này sẽ hoạt động, nhưng sẽ hiển thị thông báo đang tải trên ứng dụng bất kể tốc độ mạng. Nếu muốn tránh các thông báo lạ, bạn có thể thử phương pháp này, trong đó chúng ta lồng bộ chọn trong đoạn mã trên vào trong một lớp slow:

.slow article:empty::before {
  text-align: center;
  content: 'Loading...';
}

Từ đây, bạn có thể dùng JavaScript trong một phần tiêu đề để đọc loại kết nối hiệu quả (ít nhất là trong các trình duyệt Chromium) nhằm thêm lớp slow vào phần tử <html> trên một số loại kết nối:

<script>
  const effectiveType = navigator?.connection?.effectiveType;

  if (effectiveType !== '4g') {
    document.documentElement.classList.add('slow');
  }
</script>

Việc này đảm bảo rằng các loại kết nối hiệu quả chậm hơn loại 4g sẽ nhận được thông báo đang tải. Sau đó, trong phần nội dung một phần, bạn có thể đặt phần tử <script> cùng dòng để xoá lớp slow khỏi HTML và loại bỏ thông báo đang tải:

<script>
  document.documentElement.classList.remove('slow');
</script>

Cung cấp phản hồi dự phòng

Giả sử bạn đang sử dụng chiến lược ưu tiên mạng cho một phần nội dung. Nếu người dùng hiện không có kết nối mạng và đã truy cập vào một trang mà họ đã truy cập, thì họ sẽ được bảo vệ. Tuy nhiên, nếu họ chưa truy cập vào một trang chưa đến thì họ sẽ không nhận được gì. Để tránh điều này, bạn cần phải phân phát phản hồi dự phòng.

Mã cần thiết để đạt được phản hồi dự phòng được minh hoạ trong các mã mẫu trước. Quy trình này cần có 2 bước:

  1. Lưu trước vào bộ nhớ đệm một phản hồi dự phòng ngoại tuyến.
  2. Thiết lập lệnh gọi lại handlerDidError trong trình bổ trợ cho chiến lược ưu tiên mạng để kiểm tra bộ nhớ đệm cho phiên bản truy cập gần đây nhất của trang. Nếu chưa từng truy cập trang đó, bạn cần dùng phương thức matchPrecache từ mô-đun workbox-precaching để truy xuất phản hồi dự phòng từ bộ nhớ đệm trước.

Lưu vào bộ nhớ đệm và CDN

Nếu bạn đang sử dụng mẫu truyền trực tuyến này trong trình chạy dịch vụ, hãy đánh giá xem những điều sau đây có áp dụng cho trường hợp của bạn hay không:

  • Bạn sử dụng CDN hoặc bất kỳ loại bộ nhớ đệm trung gian/công khai nào khác.
  • Bạn đã chỉ định tiêu đề Cache-Control có(các) lệnh max-age và/hoặc s-maxage khác 0 cùng với lệnh public.

Nếu bạn gặp phải cả hai trường hợp trên, thì bộ nhớ đệm trung gian có thể giữ lại các phản hồi cho các yêu cầu điều hướng. Tuy nhiên, hãy nhớ rằng khi sử dụng mẫu này, bạn có thể phân phát hai phản hồi khác nhau cho một URL nhất định:

  • Phản hồi đầy đủ, chứa mã đánh dấu tiêu đề, nội dung và chân trang.
  • Phản hồi một phần, chỉ chứa nội dung.

Điều này có thể gây ra một số hành vi không mong muốn, dẫn đến việc mã đánh dấu ở đầu trang và chân trang bị tăng gấp đôi, vì trình chạy dịch vụ có thể đang tìm nạp phản hồi đầy đủ từ bộ nhớ đệm CDN, đồng thời kết hợp phản hồi đó với mã đánh dấu đầu trang và chân trang được lưu vào bộ nhớ đệm trước.

Để giải quyết vấn đề này, bạn sẽ cần dựa vào tiêu đề Vary. Tiêu đề này ảnh hưởng đến hoạt động lưu vào bộ nhớ đệm bằng cách khoá các phản hồi có thể lưu vào bộ nhớ đệm cho một hoặc nhiều tiêu đề đã có trong yêu cầu. Do chúng ta đang thay đổi phản hồi cho các yêu cầu điều hướng dựa trên các tiêu đề của yêu cầu Service-Worker-Navigation-PreloadX-Content-Mode tuỳ chỉnh, nên chúng ta cần chỉ định tiêu đề Vary này trong phản hồi:

Vary: Service-Worker-Navigation-Preload,X-Content-Mode

Với tiêu đề này, trình duyệt sẽ phân biệt giữa phản hồi hoàn chỉnh và một phần cho các yêu cầu điều hướng, tránh các vấn đề với mã đánh dấu đầu trang và chân trang bị nhân đôi, cũng như mọi bộ nhớ đệm trung gian.

Kết quả

Hầu hết lời khuyên về hiệu suất trong thời gian tải đều xoay quanh "cho họ thấy bạn có những gì". Đừng chần chừ, đừng đợi cho đến khi bạn có mọi thứ rồi mới cho người dùng thấy bất cứ thứ gì.

Jake Archibald trong bài viết Fun Hacks for nhanh Content

Các trình duyệt vượt trội khi xử lý phản hồi cho yêu cầu điều hướng, ngay cả với các nội dung phản hồi HTML lớn. Theo mặc định, các trình duyệt sẽ dần truyền trực tuyến và xử lý mã đánh dấu theo từng phần giúp tránh các thao tác dài. Điều này rất tốt cho hiệu suất khi khởi động.

Điều này có lợi cho chúng tôi khi sử dụng mẫu trình chạy dịch vụ truyền trực tuyến. Bất cứ khi nào bạn phản hồi một yêu cầu từ bộ nhớ đệm của trình chạy dịch vụ khi di chuyển, hoạt động bắt đầu phản hồi sẽ gần như ngay lập tức. Khi kết hợp mã đánh dấu đầu trang và chân trang được lưu trước vào bộ nhớ đệm với một phản hồi từ mạng, bạn sẽ nhận được một số ưu điểm đáng chú ý về hiệu suất:

  • Thời gian chuyển đổi byte đầu tiên (TTFB) thường sẽ giảm đáng kể, vì byte đầu tiên của phản hồi cho yêu cầu điều hướng là tức thì.
  • Thời gian hiển thị nội dung đầu tiên (FCP) sẽ hoạt động rất nhanh, vì mã đánh dấu tiêu đề được lưu trước trong bộ nhớ đệm sẽ chứa tệp tham chiếu đến một biểu định kiểu đã lưu vào bộ nhớ đệm, nghĩa là trang sẽ vẽ rất nhanh.
  • Trong một số trường hợp, Thời gian hiển thị nội dung lớn nhất (LCP) cũng có thể nhanh hơn, đặc biệt là nếu phần tử lớn nhất trên màn hình được cung cấp bởi một phần tiêu đề được lưu trước vào bộ nhớ đệm. Mặc dù vậy, chỉ cần phân phát nội dung nào đó qua bộ nhớ đệm của trình chạy dịch vụ càng sớm càng tốt song song với các tải trọng đánh dấu nhỏ hơn có thể dẫn đến LCP tốt hơn.

Việc thiết lập và lặp lại các cấu trúc phát trực tuyến nhiều trang có thể hơi phức tạp, nhưng thường không phức tạp hơn so với SPA trên lý thuyết. Lợi ích chính là bạn không thay thế lược đồ điều hướng mặc định của trình duyệt, mà bạn đang nâng cao lược đồ điều hướng đó.

Hơn nữa, Workbox giúp cho cấu trúc này không chỉ khả thi mà còn dễ dàng hơn so với khi bạn tự triển khai. Hãy dùng thử tính năng này trên trang web của chính bạn và xem trang web nhiều trang của bạn có thể giúp người dùng trong lĩnh vực này nhanh đến mức nào.

Tài nguyên