ปัจจุบันเว็บไซต์หรือแอปเว็บหากคุณต้องการ มีแนวโน้มที่จะใช้รูปแบบการนำทางแบบใดแบบหนึ่งจาก 2 แบบดังนี้
- รูปแบบการนำทางที่เบราว์เซอร์ต่างๆ มีให้โดยค่าเริ่มต้น กล่าวคือ คุณต้องป้อน URL ในแถบที่อยู่ของเบราว์เซอร์ และคำขอการนำทางจะแสดงเอกสารเป็นการตอบกลับ จากนั้นคุณคลิกที่ลิงก์ ซึ่งจะยกเลิกการโหลดเอกสารปัจจุบันสำหรับอีกลิงก์หนึ่ง นั่นคือ ad infinitum
- รูปแบบแอปพลิเคชันหน้าเว็บเดียว ซึ่งเกี่ยวข้องกับคำขอการนำทางเบื้องต้นเพื่อโหลด Application Shell และใช้ JavaScript เพื่อป้อนข้อมูล Application Shell ด้วยมาร์กอัปที่แสดงผลโดยไคลเอ็นต์ด้วยเนื้อหาจาก API แบ็กเอนด์สำหรับ "การนำทาง" แต่ละรายการ
ประโยชน์ของแต่ละแนวทางคือ
- รูปแบบการนำทางที่เบราว์เซอร์มีให้โดยค่าเริ่มต้นนั้นมีความยืดหยุ่น เนื่องจากไม่ต้องอาศัย JavaScript ในการเข้าถึง การแสดงมาร์กอัปโดยไคลเอ็นต์โดยใช้ JavaScript อาจเป็นกระบวนการที่อาจมีค่าใช้จ่ายสูงเช่นกัน ซึ่งหมายความว่าอุปกรณ์ระดับล่างอาจอยู่ในสถานการณ์ที่เนื้อหาล่าช้าเนื่องจากอุปกรณ์ถูกบล็อกการประมวลผลสคริปต์ที่แสดงเนื้อหา
- ในทางตรงกันข้าม แอปพลิเคชันหน้าเว็บเดียว (SPA) อาจให้การนำทางที่เร็วขึ้นหลังจากการโหลดครั้งแรก แทนที่จะต้องพึ่งเบราว์เซอร์ในการยกเลิกการโหลดเอกสารสำหรับเอกสารใหม่ทั้งหมด (และทำซ้ำสำหรับทุกการนำทาง) ลูกค้าของคุณสามารถนำเสนอสิ่งที่รู้สึกว่า เร็วขึ้น และ "คล้ายแอป" มากขึ้นได้ ประสบการณ์แม้ว่าจะต้องใช้ JavaScript ในการทำงานก็ตาม
ในโพสต์นี้ เราจะพูดถึงวิธีที่ 3 ที่สร้างสมดุลระหว่าง 2 แนวทางที่อธิบายไว้ข้างต้น คือการอาศัยโปรแกรมทำงานของบริการเพื่อแคชองค์ประกอบทั่วไปของเว็บไซต์ล่วงหน้า เช่น มาร์กอัปส่วนหัวและส่วนท้าย และการใช้สตรีมเพื่อให้การตอบสนอง HTML แก่ไคลเอ็นต์โดยเร็วที่สุด ขณะที่ยังคงใช้รูปแบบการนำทางเริ่มต้นของเบราว์เซอร์
ทำไมจึงต้องสตรีมการตอบกลับ HTML ใน Service Worker
สตรีมมิงเป็นสิ่งที่เว็บเบราว์เซอร์ของคุณทำอยู่แล้วเมื่อมีการส่งคำขอ สิ่งนี้มีความสำคัญอย่างยิ่งในบริบทของคำขอการนำทาง เนื่องจากจะช่วยให้มั่นใจได้ว่าเบราว์เซอร์จะไม่ถูกบล็อก เพื่อรอการตอบกลับทั้งหมดก่อนที่จะสามารถเริ่มแยกวิเคราะห์มาร์กอัปเอกสารและแสดงผลหน้า
สําหรับโปรแกรมทำงานของบริการ สตรีมมิงจะแตกต่างเล็กน้อยเนื่องจากใช้ Streams API ของ JavaScript งานที่สำคัญที่สุดที่ Service Worker ดำเนินการคือการสกัดกั้นและตอบสนองต่อคำขอ ซึ่งรวมถึงคำขอการนำทาง
คำขอเหล่านี้จะโต้ตอบกับแคชได้หลายวิธี แต่รูปแบบการแคชที่พบได้ทั่วไปสำหรับมาร์กอัปคือชอบใช้การตอบสนองจากเครือข่ายแรก แต่จะกลับไปใช้แคชหากมีสำเนาที่เก่ากว่า และให้การตอบกลับสำรองทั่วไปหากไม่มีการตอบกลับที่ใช้งานได้ในแคช
นี่เป็นรูปแบบที่ผ่านการทดสอบมายาวนานสำหรับมาร์กอัปที่ทำงานได้ดี แต่ในขณะที่จะช่วยเพิ่มความน่าเชื่อถือในแง่ของการเข้าถึงแบบออฟไลน์ แต่ก็ไม่ได้ให้ข้อได้เปรียบด้านประสิทธิภาพแฝงสำหรับคำขอการนำทางที่อาศัยกลยุทธ์แบบเครือข่ายก่อนหรือเครือข่ายเท่านั้น สตรีมมิงจะช่วยแก้ปัญหานี้ได้และเราจะสำรวจวิธีใช้โมดูล workbox-streams
ที่ขับเคลื่อนโดย Streams API ในโปรแกรมทำงานของบริการ Workbox เพื่อเพิ่มความเร็วคำขอการนำทางในเว็บไซต์ที่มีหลายหน้า
แจกแจงหน้าเว็บทั่วไป
โดยทั่วไปแล้ว เว็บไซต์มีแนวโน้มที่จะมีองค์ประกอบทั่วไปที่มีอยู่ในทุกหน้า การจัดเรียงองค์ประกอบหน้าเว็บโดยทั่วไปมักมีลักษณะดังนี้
- ส่วนหัว
- เนื้อหา
- ส่วนท้าย
เมื่อใช้ web.dev เป็นตัวอย่าง รายละเอียดขององค์ประกอบทั่วไปจะมีลักษณะดังนี้
เป้าหมายที่อยู่เบื้องหลังการระบุส่วนต่างๆ ของหน้าเว็บคือเรากำหนดว่าเนื้อหาใดที่สามารถแคชล่วงหน้าและดึงข้อมูลได้โดยไม่ต้องไปที่เครือข่าย ซึ่งก็คือมาร์กอัปส่วนหัวและส่วนท้ายที่นิยมใช้กันในทุกหน้า และส่วนหนึ่งของหน้าเว็บที่เราจะไปที่เครือข่ายก่อนเสมอคือเนื้อหาในกรณีนี้
เมื่อรู้วิธีแบ่งกลุ่มส่วนต่างๆ ของหน้าและระบุองค์ประกอบทั่วไปแล้ว เราสามารถเขียน Service Worker ที่เรียกมาร์กอัปส่วนหัวและส่วนท้ายได้ทันทีจากแคชเสมอ ขณะที่ขอเฉพาะเนื้อหาจากเครือข่าย
จากนั้นเราใช้ Streams API ผ่าน workbox-streams
ต่อชิ้นส่วนเหล่านี้ทั้งหมดเข้าด้วยกัน และตอบสนองคำขอการนำทางได้ทันที พร้อมกับขอมาร์กอัปในจำนวนขั้นต่ำที่จำเป็นจากเครือข่าย
การสร้างโปรแกรมทำงานของบริการสตรีมมิง
เมื่อสตรีมเนื้อหาบางส่วนใน Service Worker มีหลายส่วนที่ต้องเคลื่อนไหว แต่เราจะสำรวจกระบวนการในแต่ละขั้นตอนอย่างละเอียดเมื่อคุณเริ่มวางโครงสร้างเว็บไซต์
การแบ่งกลุ่มเว็บไซต์ของคุณออกเป็นส่วนๆ
ก่อนที่จะเริ่มเขียนโปรแกรมทำงานของบริการสตรีมมิงได้ คุณจะต้องทำ 3 สิ่งต่อไปนี้
- สร้างไฟล์ที่มีเฉพาะมาร์กอัปส่วนหัวของเว็บไซต์
- สร้างไฟล์ที่มีเฉพาะมาร์กอัปส่วนท้ายของเว็บไซต์
- ดึงเนื้อหาหลักของแต่ละหน้าออกมาเป็นไฟล์แยกต่างหาก หรือตั้งค่าระบบแบ็กเอนด์ให้แสดงเฉพาะเนื้อหาของหน้าโดยมีเงื่อนไขโดยอิงจากส่วนหัวของคำขอ HTTP
อย่างที่คุณคาดไว้ ขั้นตอนสุดท้ายคือขั้นตอนที่ยากที่สุด โดยเฉพาะหากเว็บไซต์ไม่มีการเปลี่ยนแปลง ในกรณีนี้คุณจะต้องสร้างหน้าเว็บ 2 เวอร์ชัน โดยแต่ละเวอร์ชันจะมีมาร์กอัปหน้าแบบเต็ม ส่วนอีกเวอร์ชันจะมีเฉพาะเนื้อหา
การเขียน Service Worker สตรีมมิง
หากยังไม่ได้ติดตั้งโมดูล workbox-streams
คุณจะต้องดำเนินการดังกล่าวเสริมด้วยโมดูล Workbox ใดก็ตามที่คุณติดตั้งอยู่ในปัจจุบัน สำหรับตัวอย่างเฉพาะนี้ ซึ่งเกี่ยวข้องกับแพ็กเกจต่อไปนี้
npm i workbox-navigation-preload workbox-strategies workbox-routing workbox-precaching workbox-streams --save
จากที่นี่ ขั้นตอนถัดไปคือการสร้าง Service Worker ใหม่และแคชบางส่วนของส่วนหัวและส่วนท้ายไว้ล่วงหน้า
กำลังแคชบางส่วนล่วงหน้า
สิ่งแรกที่คุณต้องทำคือสร้าง Service Worker ในรูทของโปรเจ็กต์ชื่อ sw.js
(หรือชื่อไฟล์ใดก็ได้ที่ต้องการ) ในนั้น จะเริ่มด้วยสิ่งต่อไปนี้
// 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...
โค้ดนี้ทำหน้าที่ 2-3 อย่าง:
- เปิดใช้การโหลดการนำทางล่วงหน้าสำหรับเบราว์เซอร์ที่รองรับ
- แคชล่วงหน้าสำหรับมาร์กอัปส่วนหัวและส่วนท้าย ซึ่งหมายความว่าระบบจะเรียกข้อมูลมาร์กอัปส่วนหัวและส่วนท้ายสำหรับทุกหน้าโดยทันที เนื่องจากเครือข่ายจะไม่บล็อกมาร์กอัปดังกล่าว
- แคชชิ้นงานแบบคงที่ล่วงหน้าในตัวยึดตำแหน่ง
__WB_MANIFEST
ที่ใช้เมธอดinjectManifest
คำตอบสตรีมมิง
การทำให้โปรแกรมทำงานของบริการสตรีมคำตอบที่เชื่อมถึงกันคือส่วนสำคัญที่สุดของความพยายามนี้ แต่ถึงอย่างนั้น Workbox และ workbox-streams
ก็ทำให้งานนี้กระชับกว่าการที่คุณต้องทำทุกอย่างด้วยตัวเอง
// 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.
โค้ดนี้ประกอบด้วยส่วนหลัก 3 ส่วนซึ่งเป็นไปตามข้อกำหนดต่อไปนี้
- ใช้กลยุทธ์
NetworkFirst
เพื่อจัดการคำขอสำหรับเนื้อหาบางส่วน เมื่อใช้กลยุทธ์นี้ ระบบจะระบุชื่อแคชที่กำหนดเองของcontent
ให้มีบางส่วนของเนื้อหา รวมทั้งปลั๊กอินที่กำหนดเองซึ่งจัดการว่าจะตั้งค่าส่วนหัวของคำขอX-Content-Mode
สำหรับเบราว์เซอร์ที่ไม่รองรับการโหลดการนำทางล่วงหน้าหรือไม่ (ดังนั้นจึงไม่ส่งส่วนหัวService-Worker-Navigation-Preload
) ปลั๊กอินนี้ยังคำนวณด้วยว่าจะส่งเวอร์ชันแคชล่าสุดของเนื้อหาบางส่วน หรือส่งหน้าสำรองแบบออฟไลน์ในกรณีที่ไม่ได้เก็บเวอร์ชันแคชสำหรับคำขอปัจจุบันไว้ - เมธอด
strategy
ในworkbox-streams
(ใช้นามแฝงเป็นcomposeStrategies
ที่นี่) ใช้เพื่อต่อส่วนส่วนหัวและส่วนท้ายที่เก็บแคชล่วงหน้าไว้รวมกับเนื้อหาบางส่วนที่ขอจากเครือข่าย - ระบบจะเชื่อมโยงรูปแบบทั้งหมดผ่าน
registerRoute
สำหรับคำขอการนำทาง
เราจึงตั้งค่าสตรีมมิงการตอบกลับด้วยตรรกะนี้ อย่างไรก็ตาม คุณอาจต้องดำเนินการบางอย่างจากระบบแบ็กเอนด์เพื่อให้แน่ใจว่าเนื้อหาจากเครือข่ายเป็นส่วนหนึ่งของหน้าเว็บที่คุณสามารถผสานรวมเข้ากับข้อมูลบางส่วนที่แคชล่วงหน้าได้
หากเว็บไซต์ของคุณมีแบ็กเอนด์
คุณจะจำได้ว่าเมื่อเปิดใช้งานการโหลดการนำทางล่วงหน้า เบราว์เซอร์จะส่งส่วนหัว Service-Worker-Navigation-Preload
ที่มีค่า true
อย่างไรก็ตาม ในตัวอย่างโค้ดข้างต้น เราได้ส่งส่วนหัวที่กำหนดเองของ X-Content-Mode
ในการโหลดการนำทางเหตุการณ์ล่วงหน้าในเบราว์เซอร์ไม่รองรับ ในเบื้องหลัง คุณจะต้องเปลี่ยนการตอบกลับตามการมีส่วนหัวเหล่านี้ ในระบบแบ็กเอนด์ของ PHP หน้าดังกล่าวอาจมีลักษณะดังนี้
<?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();
}
?>
ในตัวอย่างข้างต้น เนื้อหาบางส่วนจะเรียกใช้เป็นฟังก์ชัน ซึ่งใช้ค่า $isPartial
เพื่อเปลี่ยนแปลงวิธีแสดงผลบางส่วน ตัวอย่างเช่น ฟังก์ชันตัวแสดงผล content
อาจรวมมาร์กอัปบางอย่างในเงื่อนไขเมื่อดึงข้อมูลเพียงบางส่วนเท่านั้น ซึ่งเป็นสิ่งที่จะกล่าวถึงในอีกไม่ช้า
ข้อควรพิจารณา
ก่อนติดตั้งใช้งาน Service Worker เพื่อสตรีมและเย็บส่วนต่างๆ บางส่วนเข้าด้วยกัน คุณต้องพิจารณาบางสิ่งต่อไปนี้ แม้ว่าจะจริงอยู่ที่การใช้โปรแกรมทำงานของบริการด้วยวิธีนี้ไม่ได้เปลี่ยนลักษณะการทำงานของการนำทางเริ่มต้นของเบราว์เซอร์โดยพื้นฐาน แต่ก็มีบางสิ่งที่คุณอาจต้องแก้ไข
กำลังอัปเดตองค์ประกอบของหน้าเมื่อไปยังส่วนต่างๆ
ส่วนที่ยากที่สุดของวิธีการนี้คือ จะต้องมีการอัปเดตข้อมูลบางอย่างในไคลเอ็นต์ ตัวอย่างเช่น มาร์กอัปส่วนหัวที่แคชล่วงหน้าหมายความว่าหน้าเว็บจะมีเนื้อหาเดียวกันในองค์ประกอบ <title>
หรือแม้กระทั่งจัดการสถานะเปิด/ปิดสำหรับรายการการนำทางก็จะต้องอัปเดตในการนำทางแต่ละครั้ง ข้อมูลเหล่านี้ (และอื่นๆ) อาจต้องได้รับการอัปเดตในไคลเอ็นต์สำหรับคำขอการนำทางแต่ละรายการ
วิธีหลีกเลี่ยงปัญหานี้คือวางองค์ประกอบ <script>
ในบรรทัดลงในบางส่วนของเนื้อหาที่มาจากเครือข่ายเพื่ออัปเดตสิ่งสำคัญบางอย่าง ดังนี้
<!-- The JSON below contains information about the current page. -->
<script id="page-data" type="application/json">'{"title":"Sand Wasp — 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>
นี่เป็นเพียงตัวอย่างหนึ่งของสิ่งที่คุณอาจต้องทำหากเลือกที่จะตั้งค่า Service Worker นี้ เช่น สำหรับแอปพลิเคชันที่ซับซ้อนขึ้นซึ่งมีข้อมูลผู้ใช้ คุณอาจต้องเก็บข้อมูลที่เกี่ยวข้องเล็กๆ น้อยๆ ไว้ในเว็บสโตร์ เช่น localStorage
และอัปเดตหน้าเว็บจากที่นั่น
การจัดการเครือข่ายที่ช้า
ข้อเสียอย่างหนึ่งของการสตรีมคำตอบที่ใช้มาร์กอัปจากแคชล่วงหน้าอาจเกิดขึ้นเมื่อการเชื่อมต่อเครือข่ายช้า ปัญหาคือมาร์กอัปส่วนหัวจาก Precache จะมาถึงทันที แต่เนื้อหาบางส่วนจากเครือข่ายอาจใช้เวลานานพอสมควรกว่าจะมาถึงหลังจากที่การแสดงผลครั้งแรกของมาร์กอัปส่วนหัว
ซึ่งอาจทำให้เกิดความสับสนและหากเครือข่ายทำงานช้ามาก อาจทำให้รู้สึกเหมือนหน้าเว็บแตกและไม่แสดงผลอีกเลย ในกรณีเช่นนี้ คุณสามารถเลือกวางไอคอนหรือข้อความที่กำลังโหลดลงในมาร์กอัปบางส่วนของเนื้อหาที่คุณสามารถซ่อนได้เมื่อเนื้อหาโหลดเสร็จแล้ว
ซึ่งวิธีหนึ่งที่ทำได้คือการดำเนินการผ่าน CSS สมมติว่าส่วนหัวของคุณลงท้ายด้วยองค์ประกอบ <article>
ที่เปิดอยู่ซึ่งว่างเปล่าจนกว่าเนื้อหาบางส่วนจะมาถึงเพื่อเติมข้อมูล คุณสามารถเขียนกฎ CSS ที่มีลักษณะดังนี้
article:empty::before {
text-align: center;
content: 'Loading...';
}
ซึ่งจะใช้ได้แต่จะแสดงข้อความกำลังโหลดบนไคลเอ็นต์ไม่ว่าเครือข่ายจะมีความเร็วเท่าใดก็ตาม หากต้องการเลี่ยงการแสดง Flash ของข้อความแปลกๆ ให้ลองใช้วิธีนี้เมื่อเราฝังตัวเลือกในข้อมูลโค้ดด้านบนไว้ในคลาส slow
.slow article:empty::before {
text-align: center;
content: 'Loading...';
}
จากที่นี่ คุณสามารถใช้ JavaScript ในส่วนหัวบางส่วนเพื่ออ่านประเภทการเชื่อมต่อที่มีประสิทธิภาพ (อย่างน้อยในเบราว์เซอร์ Chromium) เพื่อเพิ่มคลาส slow
ไปยังองค์ประกอบ <html>
ในการเชื่อมต่อบางประเภท ดังนี้
<script>
const effectiveType = navigator?.connection?.effectiveType;
if (effectiveType !== '4g') {
document.documentElement.classList.add('slow');
}
</script>
วิธีนี้จะช่วยรับประกันว่าประเภทการเชื่อมต่อที่มีประสิทธิภาพซึ่งช้ากว่าประเภท 4g
จะได้รับข้อความการโหลด จากนั้นในเนื้อหาบางส่วน คุณสามารถวางองค์ประกอบ <script>
ในบรรทัดเพื่อนำคลาส slow
ออกจาก HTML เพื่อกำจัดข้อความการโหลด โดยทำดังนี้
<script>
document.documentElement.classList.remove('slow');
</script>
การแสดงการตอบกลับสำรอง
สมมติว่าคุณกำลังใช้กลยุทธ์ที่เน้นเครือข่ายเป็นอันดับแรกสำหรับเนื้อหาบางส่วน หากผู้ใช้ออฟไลน์อยู่และไปที่หน้าที่เคยเข้าชมแล้ว ระบบจะปิดหน้านี้ แต่หากผู้ใช้ไปยังหน้าเว็บที่ยังไม่เคยเข้าชม ก็จะไม่มีอะไรเลย หากไม่ต้องการให้เป็นเช่นนั้น คุณจะต้องแสดงการตอบกลับสำรอง
โค้ดที่ต้องใช้ในการตอบสนองทางเลือกจะแสดงไว้ในตัวอย่างโค้ดก่อนหน้านี้ กระบวนการนี้มี 2 ขั้นตอนดังนี้
- แคชการตอบกลับสำรองแบบออฟไลน์ไว้ล่วงหน้า
- ตั้งค่า
handlerDidError
Callback ในปลั๊กอินสำหรับกลยุทธ์แบบเน้นเครือข่ายเป็นหลัก เพื่อตรวจสอบแคชของหน้าเว็บเวอร์ชันที่มีการเข้าถึงล่าสุด หากไม่เคยเข้าถึงหน้าดังกล่าว คุณจะต้องใช้เมธอดmatchPrecache
จากโมดูลworkbox-precaching
เพื่อเรียกการตอบกลับสำรองจากแคชล่วงหน้า
การแคชและ CDN
หากคุณใช้รูปแบบสตรีมมิงนี้ใน Service Worker ให้ประเมินว่ารายการต่อไปนี้มีผลกับสถานการณ์ของคุณหรือไม่
- คุณใช้ CDN หรือแคชกลาง/สาธารณะประเภทอื่นๆ
- คุณได้ระบุส่วนหัว
Cache-Control
ด้วยคำสั่งmax-age
ที่ไม่ใช่ 0 และ/หรือs-maxage
ร่วมกับคำสั่งpublic
หากทั้ง 2 กรณีนี้เป็นกรณีของคุณ แคชกลางอาจกักเก็บการตอบกลับคำขอการนำทาง อย่างไรก็ตาม โปรดทราบว่าเมื่อคุณใช้รูปแบบนี้ คุณอาจจะแสดงการตอบกลับที่แตกต่างกันสองชุดสำหรับ URL หนึ่งๆ:
- การตอบกลับแบบเต็ม ซึ่งมีมาร์กอัปส่วนหัว เนื้อหา และส่วนท้าย
- คำตอบบางส่วนที่มีเฉพาะเนื้อหา
การทำเช่นนี้อาจทำให้เกิดการทำงานที่ไม่พึงประสงค์บางอย่าง ซึ่งส่งผลให้เกิดมาร์กอัปส่วนหัวและส่วนท้ายเพิ่มเป็น 2 เท่า เนื่องจาก Service Worker อาจดึงข้อมูลการตอบสนองเต็มรูปแบบจากแคช CDN และรวมข้อมูลดังกล่าวเข้ากับมาร์กอัปส่วนหัวและส่วนท้ายที่เก็บไว้ล่วงหน้า
หากต้องการแก้ปัญหานี้ คุณจะต้องใช้ส่วนหัว Vary
ซึ่งส่งผลต่อลักษณะการแคชด้วยคีย์การตอบกลับที่แคชได้กับส่วนหัวอย่างน้อย 1 รายการที่มีอยู่ในคําขอ เนื่องจากเราจะเปลี่ยนแปลงการตอบกลับคำขอการนำทางโดยอิงตามส่วนหัวของคำขอ Service-Worker-Navigation-Preload
และ X-Content-Mode
ที่กำหนดเอง เราจึงต้องระบุส่วนหัว Vary
นี้ในการตอบ
Vary: Service-Worker-Navigation-Preload,X-Content-Mode
เมื่อใช้ส่วนหัวนี้ เบราว์เซอร์จะแยกความแตกต่างระหว่างการตอบกลับที่สมบูรณ์และบางส่วนสำหรับคำขอการนำทาง หลีกเลี่ยงปัญหาเกี่ยวกับมาร์กอัปส่วนหัวและส่วนท้ายที่เพิ่มขึ้นเป็นสองเท่า เช่นเดียวกับแคชกลาง
ผลลัพธ์
คำแนะนำเกี่ยวกับประสิทธิภาพเวลาที่ใช้ในการโหลดส่วนใหญ่จะมุ่งเน้นที่ "แสดงสิ่งที่คุณได้รับ" เท่านั้น อย่ายอมแพ้ อย่ารอจนกว่าคุณจะมีทุกสิ่งทุกอย่างครบก่อนที่จะแสดงให้ผู้ใช้เห็น
Jake Archibald ในเคล็ดลับสนุกๆ เพื่อเนื้อหาที่รวดเร็วขึ้น
เบราว์เซอร์ทำงานได้ดีเมื่อต้องจัดการการตอบสนองคำขอการนำทาง แม้จะเป็นการตอบสนองของ HTML ที่มีขนาดใหญ่ก็ตาม โดยค่าเริ่มต้น เบราว์เซอร์จะสตรีมและประมวลผลมาร์กอัปเป็นช่วงๆ อย่างต่อเนื่อง ซึ่งจะหลีกเลี่ยงงานที่ใช้เวลานาน ซึ่งส่งผลดีต่อประสิทธิภาพของสตาร์ทอัพ
ซึ่งเป็นข้อได้เปรียบของเราเมื่อเราใช้รูปแบบ Service Worker สตรีมมิง เมื่อใดก็ตามที่คุณตอบกลับคำขอจากแคชของ Service Worker ตั้งแต่ต้น การตอบกลับจะเริ่มต้นแทบจะในทันที เมื่อรวมมาร์กอัปส่วนหัวและส่วนท้ายที่แคชล่วงหน้าเข้ากับการตอบกลับจากเครือข่าย คุณจะเห็นข้อได้เปรียบด้านประสิทธิภาพที่โดดเด่นดังต่อไปนี้
- Time to First Byte (TTFB) มักจะลดลงอย่างมากเนื่องจากไบต์แรกของการตอบกลับคำขอการนำทางเป็นแบบทันที
- First Contentful Paint (FCP) จะทำงานเร็วมากเนื่องจากมาร์กอัปส่วนหัวที่แคชล่วงหน้าจะมีการอ้างอิงไปยังสไตล์ชีตที่แคชไว้ ซึ่งหมายความว่าหน้าเว็บจะแสดงผลได้รวดเร็วมาก
- ในบางกรณี การแสดงผลเนื้อหาขนาดใหญ่ที่สุด (LCP) ก็ทำงานได้เร็วขึ้นเช่นกัน โดยเฉพาะอย่างยิ่งหากส่วนหัวบางส่วนที่แคชไว้ล่วงหน้าเป็นองค์ประกอบที่ใหญ่ที่สุดบนหน้าจอ อย่างไรก็ตาม การแสดงบางอย่างจากแคชของ Service Worker ให้เร็วที่สุดควบคู่กับเพย์โหลดมาร์กอัปที่เล็กลงอาจส่งผลให้ LCP ดีขึ้น
สถาปัตยกรรมสตรีมมิงแบบหลายหน้าอาจยุ่งยากเล็กน้อยในการตั้งค่าและทำซ้ำ แต่ความซับซ้อนที่เกี่ยวข้องมักไม่ยุ่งยากไปกว่า SPA ในทางทฤษฎี ประโยชน์หลักก็คือ คุณไม่ได้แทนที่รูปแบบการนำทางเริ่มต้นของเบราว์เซอร์ แต่คุณกำลังปรับปรุงให้ดีขึ้น
ยิ่งไปกว่านั้น Workbox ทำให้สถาปัตยกรรมนี้ไม่เพียงแต่เป็นไปได้ยากเท่านั้น แต่ยังทำได้ง่ายกว่าการที่คุณนำไปใช้งานด้วยตัวเอง ลองใช้เว็บไซต์ของคุณเอง แล้วดูว่าเว็บไซต์ที่มีหลายหน้าจะเร็วขึ้นสำหรับผู้ใช้ในภาคสนามได้มากน้อยเพียงใด