כיום, באתרים או באפליקציות באינטרנט, אם אתם מעדיפים, בדרך כלל משתמשים באחת משתי סכימות ניווט:
- הדפדפנים עם סכימת הניווט מספקים כברירת מחדל. כלומר, מזינים כתובת URL בסרגל הכתובות של הדפדפן ובקשת ניווט מחזירה מסמך כתשובה. לאחר מכן לוחצים על קישור, שמסיר את הטעינה של המסמך הנוכחי במסמך אחר – מודעות אינסופיות.
- דפוס האפליקציה בדף יחיד, שכולל בקשת ניווט ראשונית לטעינת מעטפת האפליקציה, ומסתמך על JavaScript כדי לאכלס את מעטפת האפליקציה בתגי עיצוב שעובדו על ידי לקוח עם תוכן מ-API בקצה עורפי לכל 'ניווט'.
התומכים שלה ציינו את היתרונות של כל גישה:
- סכמת הניווט שדפדפנים מספקים כברירת מחדל היא עמידה, כי המסלולים לא מחייבים שימוש ב-JavaScript. גם עיבוד של תגי עיצוב על ידי לקוח באמצעות JavaScript עשוי להיות תהליך יקר. משמעות הדבר היא שמכשירים פחות מתקדמים עלולים להגיע למצב שבו התוכן מתעכב מפני שהמכשיר חסום לעיבוד סקריפטים שמספקים תוכן.
- לעומת זאת, אפליקציות בדף יחיד (SPA) עשויות לספק ניווטים מהירים יותר לאחר הטעינה הראשונית. במקום להסתמך על הדפדפן כדי להסיר מסמך שנטענו עבור מסמך חדש לגמרי (וחוזרים על כך בכל ניווט), הם יכולים להציע תהליך מהיר יותר שדומה יותר לאפליקציה. - גם אם JavaScript נדרש כדי לפעול.
בפוסט הזה נדבר על שיטה שלישית ששומרת על איזון בין שתי הגישות שתוארו למעלה: הסתמכת על קובץ שירות (service worker) כדי לשמור מראש את הרכיבים הנפוצים של אתר, כמו תגי עיצוב של כותרת עליונה ותחתונות, ושימוש בזרמים כדי לספק ללקוח תגובת HTML במהירות האפשרית, תוך שימוש בסכמת הניווט המוגדרת כברירת מחדל בדפדפן.
למה כדאי להזרים תגובות HTML ב-Service Worker?
סטרימינג הוא משהו שדפדפן האינטרנט כבר עושה כשהוא שולח בקשות. זה חשוב ביותר בהקשר של בקשות ניווט, כי הוא מבטיח שהדפדפן לא ייחסם עד שיסתיים התגובה לפני שהיא תוכל להתחיל לנתח את תגי העיצוב של המסמך ולעבד דף.
ב-Service Works API, הסטרימינג הוא קצת שונה כי הוא משתמש ב-Streams API של JavaScript. המשימה הכי חשובה שמשתמשי שירות מבצעים היא ליירט בקשות ולהגיב להן, כולל בקשות לניווט.
הבקשות האלה יכולות ליצור אינטראקציה עם המטמון במספר דרכים, אבל דפוס שמירה במטמון נפוץ של תגי עיצוב הוא להעדיף שימוש בתגובה מהרשת הראשונה, אבל לעבור חזרה למטמון אם יש עותק ישן יותר, ויש אפשרות לספק תגובה חלופית כללית אם תגובה שניתנת לשימוש לא נמצאת במטמון.
זהו דפוס של תגי עיצוב שנבדק בזמן והוא פועל היטב, אבל הוא אמנם עוזר לשמור על האמינות מבחינת גישה אופליין, אבל הוא לא מציע יתרונות ביצועים מהותיים בבקשות ניווט שמסתמכות על אסטרטגיה אחת ברשת או על רשת בלבד. כאן נכנסים לתמונה סטרימינג, ואנחנו נראה לכם איך להשתמש במודול workbox-streams
שמבוסס על ממשק ה-API של Streamer ב-Workbox Serviceer (רכיב שירות) של Workbox כדי להאיץ בקשות ניווט באתר שלכם מרובה דפים.
פירוט של דף אינטרנט אופייני
מבחינה מבנית, לאתרים בדרך כלל יש אלמנטים משותפים שקיימים בכל דף. סידור אופייני של רכיבי הדף בדרך כלל מתבצע כך:
- כותרת.
- תוכן.
- כותרת תחתונה.
נשתמש ב-web.dev כדוגמה, הפירוט של רכיבים נפוצים נראה כך:
![פירוט של הרכיבים הנפוצים באתר web.dev. האזורים המשותפים שמוקפים מסומנים כ'כותרת עליונה', 'תוכן' ו'כותרת תחתונה'.](https://developer.chrome.com/static/docs/workbox/faster-multipage-applications-with-streams/image/a-breakdown-the-common-e-a093e46f9c2d1.png?authuser=9&hl=he)
המטרה שעומדת מאחורי זיהוי של חלקים בדף היא שאנחנו קובעים מה אפשר לשמור מראש במטמון ולאחזר בלי לעבור לרשת - כלומר, תגי העיצוב של כותרת עליונה ותחתונות שמשותפים לכל הדפים — והחלק בדף שאליו תמיד נפנה לרשת הראשונה — התוכן במקרה זה.
כשאנחנו יודעים איך לפלח את חלקי הדף ולזהות את הרכיבים הנפוצים, אנחנו יכולים לכתוב Service Worker שמאחזר את תגי העיצוב של הכותרת העליונה והכותרת התחתונה באופן מיידי מהמטמון, תוך בקשה רק של התוכן מהרשת.
לאחר מכן, באמצעות ה-Stream API דרך workbox-streams
, נוכל לחבר את כל החלקים האלה יחד ולהגיב לבקשות ניווט באופן מיידי — תוך בקשה בכמות המינימלית הדרושה של תגי עיצוב מהרשת.
פיתוח Worker של שירות סטרימינג
התהליך של סטרימינג של תוכן חלקי ב-service worker כולל הרבה חלקים, אבל נסביר בפירוט על כל שלב בתהליך, החל מאופן בניית האתר.
פילוח האתר לחלקים
לפני שתוכלו להתחיל לכתוב לעבוד עם שירות סטרימינג, עליכם לעשות שלושה דברים:
- יוצרים קובץ שמכיל רק את תגי העיצוב לכותרות של האתר שלכם.
- יוצרים קובץ שמכיל רק את תגי העיצוב של הכותרת התחתונה באתר.
- שולפים את התוכן הראשי של כל דף לקובץ נפרד, או מגדירים את הקצה העורפי כך שיציג רק את תוכן הדף באופן מותנה, בהתאם לכותרת של בקשת ה-HTTP.
כפי שאפשר לצפות, השלב האחרון הוא הקשה ביותר, במיוחד אם האתר סטטי. במקרה כזה, עליכם ליצור שתי גרסאות לכל דף: גרסה אחת תכיל את תגי העיצוב המלאים של הדף, והשנייה תכיל רק את התוכן.
כתיבת Worker של שירות סטרימינג
אם לא התקנתם את המודול workbox-streams
, תצטרכו לעשות זאת בנוסף למודולים של Workbox שמותקנים אצלכם כרגע. בדוגמה הספציפית הזו, החבילות כוללות את החבילות הבאות:
npm i workbox-navigation-preload workbox-strategies workbox-routing workbox-precaching workbox-streams --save
מכאן, השלב הבא הוא ליצור את ה-Service Worker החדש ולשמור מראש את החלקי של הכותרת העליונה והכותרת התחתונה במטמון.
מתבצעת שמירה של חלקים במטמון
בשלב הראשון יוצרים קובץ Service Worker ברמה הבסיסית (root) של הפרויקט בשם 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...
הקוד הזה מבצע כמה פעולות:
- ההגדרה הזו מפעילה טעינה מראש של הניווט לדפדפנים שתומכים בכך.
- שמירה מראש של תגי העיצוב של הכותרת העליונה והכותרת התחתונה. המשמעות היא שתגי העיצוב של הכותרת העליונה והכותרת התחתונה של כל דף מאוחזרים מיד, כי הרשת לא תחסום אותם.
- שמירה מראש של נכסים סטטיים ב-placeholder של
__WB_MANIFEST
שמשתמש ב-methodinjectManifest
.
הצגת תשובות באופן שוטף
החלק הכי גדול בכל המאמץ הזה הוא לגרום ל-Service Worker לשדר תשובות בשרשור. למרות זאת, השימוש ב-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.
הקוד הזה מורכב משלושה חלקים עיקריים שעומדים בדרישות הבאות:
- אסטרטגיית
NetworkFirst
משמשת לטיפול בבקשות לחלקי תוכן. בשיטה הזו מוגדר שם מטמון מותאם אישית שלcontent
שיכיל את חלקי התוכן, ופלאגין מותאם אישית שמטפל בשאלה אם להגדיר כותרת בקשה שלX-Content-Mode
לדפדפנים שלא תומכים בטעינה מראש של ניווט (ולכן לא שולחים כותרתService-Worker-Navigation-Preload
). הפלאגין הזה גם קובע אם לשלוח את הגרסה האחרונה שנשמרה במטמון של תוכן חלקי, או לשלוח דף חלופי במצב אופליין במקרה שלא שמורה גרסה שנשמרה במטמון של הבקשה הנוכחית. - ה-method
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
עשויה לכלול רק תגי עיצוב מסוימים בתנאים אם הם מאוחזרים כחלקיים, והם ייכללו בקרוב.
לתשומת ליבכם
לפני שאתם פורסים Worker כדי לבצע סטרימינג ולחבר חלקיקים ביחד, יש כמה דברים שצריך לקחת בחשבון. זה נכון ששימוש ב-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>
זו רק דוגמה אחת למה שצריך לעשות אם מחליטים להשתמש בהגדרת קובץ השירות הזה. באפליקציות מורכבות יותר שכוללות פרטי משתמשים, למשל, יכול להיות שתצטרכו לאחסן קטעי נתונים רלוונטיים בחנות אינטרנט כמו localStorage
ולעדכן את הדף משם.
התמודדות עם רשתות איטיות
חסרון אחד של תגובות בסטרימינג באמצעות תגי עיצוב מהמטמון מראש יכול לקרות כשהחיבור לרשת איטי. הבעיה היא שתגי העיצוב של הכותרת מהמטמון מראש יגיעו באופן מיידי, אבל יכול להיות שייקח זמן מה עד שהתוכן החלקי מהרשת יגיע אחרי ציור הכותרת הראשונית.
מצב כזה עלול ליצור חוויה מבלבלת. אם הרשתות איטיות מאוד, ייתכן שגם תרגישו שהדף לא תקין והרינדור לא מתבצע עוד. במקרים כאלה, תוכלו לבחור להוסיף סמל טעינה או הודעה לתגי העיצוב של התוכן החלקי, ותוכלו להסתיר אותם אחרי שהתוכן ייטען.
אחת מהדרכים לעשות את זה היא באמצעות שירות CSS. נניח שהכותרת החלקית מסתיימת ברכיב <article>
פותח ריק, עד שחלק מהתוכן מגיע כדי לאכלס אותו. אפשר לכתוב כלל CSS דומה לזה:
article:empty::before {
text-align: center;
content: 'Loading...';
}
הפעולה הזאת פועלת, אבל אצל הלקוח תוצג הודעת טעינה, ללא קשר למהירות הרשת. אם אתם רוצים למנוע הבזק מוזר של מסרים, אפשר לנסות את הגישה הבאה, שבה אנחנו ממקמים את הבורר בקטע הקוד שלמעלה בתוך מחלקה 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>
מתן תשובה חלופית
נניח שאתם משתמשים באסטרטגיה שמתמקדת ברשת בעיקר בשביל חלקי תוכן. אם המשתמש לא מחובר לאינטרנט ועובר לדף שהוא כבר נכנס אליו, המשמעות היא שהוא מוסתר. עם זאת, אם הוא יעבור לדף שעדיין לא נכנס אליו, הוא לא יקבל כלום. כדי למנוע זאת, יהיה צורך להציג תגובה חלופית.
הקוד שנדרש כדי לקבל תשובה חלופית מודגם בדוגמאות קוד קודמות. התהליך כולל שני שלבים:
- שמירה מראש של תגובה חלופית במצב אופליין.
- מגדירים קריאה חוזרת (callback) של
handlerDidError
בפלאגין לאסטרטגיית המיקוד ברשת, כדי לבדוק את המטמון של גרסת הדף האחרונה שניגשה אליה. אם לא בוצעה גישה לדף, צריך להשתמש בmethodmatchPrecache
מהמודולworkbox-precaching
כדי לאחזר את התשובה החלופית מהמטמון מראש.
שמירה במטמון ורשתות CDN
אם אתם משתמשים בדפוס הסטרימינג הזה ב-Service Worker, נסו לבדוק אם האפשרויות הבאות רלוונטיות למצב שלכם:
- אתם משתמשים ב-CDN או בכל סוג אחר של מטמון ביניים או מטמון ציבורי.
- ציינת כותרת
Cache-Control
עם הוראות שאינן אפסmax-age
ו/אוs-maxage
בשילוב עם ההוראהpublic
.
אם שני הדברים האלה רלוונטיים לכם, יכול להיות שמטמון הביניים שומר את התגובות לבקשות ניווט. עם זאת, חשוב לזכור שכאשר משתמשים בדפוס הזה, עשויות להופיע שתי תגובות שונות לכל כתובת URL נתונה:
- התגובה המלאה, שמכילה את תגי העיצוב של הכותרת העליונה, התוכן והכותרת התחתונה.
- התשובה החלקית, שמכילה רק את התוכן.
הדבר עלול לגרום להתנהגויות לא רצויות ולגרום לתגי עיצוב כפולים של כותרת עליונה ותחתונות, מפני שה-Service Worker עשוי לאחזר תגובה מלאה ממטמון ה-CDN ולשלב אותה בתגי העיצוב של הכותרת העליונה והכותרת התחתונה שנשמרו מראש במטמון.
כדי לעקוף את הבעיה, צריך להסתמך על הכותרת Vary
. המדיניות הזו משפיעה על התנהגות השמירה במטמון על ידי צירוף של תגובות שניתנות לשמירה במטמון לכותרת אחת או יותר שנכללו בבקשה. אנחנו משנים את התגובות לבקשות ניווט על סמך כותרות Service-Worker-Navigation-Preload
וכותרות מותאמות אישית מסוג X-Content-Mode
, לכן צריך לציין את הכותרת Vary
הבאה בתגובה:
Vary: Service-Worker-Navigation-Preload,X-Content-Mode
באמצעות הכותרת העליונה, הדפדפן יבחין בין תגובות מלאות לבקשות ניווט, וימנע בעיות הקשורות לסימון כפול של תגי עיצוב של כותרת עליונה ותחתונות, וכן מטמון ביניים.
התוצאה
העצות בנושא ביצועים של זמן טעינה מרוכזות בדרך כלל בנוסח הבא: "הראו להם מה זכיתם" — לא כדאי להתעכב, לא להמתין עד שיש לכם את כל המידע לפני שמראים למשתמש משהו.
ג'ייק ארצ'יבלד (Jake Archibald) בסרטון Fun Hacks for Faster Content
לדפדפנים יש יתרון בכל הנוגע להתמודדות עם תגובות לבקשות ניווט, אפילו כשמדובר בגופי תגובה עצומים של HTML. כברירת מחדל, הדפדפנים משדרים ומעבדים בהדרגה את תגי העיצוב במקטעים שנמנעים ממשימות ארוכות. הפעולה הזו משפרת את ביצועי ההפעלה.
זה עובד לטובתנו כשאנחנו משתמשים בתבנית Worker של שירות סטרימינג. בכל פעם שמגיבים לבקשה מהמטמון של Service Worker כבר מההתחלה, תחילת התגובה מגיעה כמעט באופן מיידי. כשמחברים בין תגי עיצוב של כותרת עליונה וכותרת תחתונה שנשמרו מראש לבין תגובה מהרשת, אפשר ליהנות מכמה יתרונות ביצועים בולטים:
- זמן עד בייט ראשון (TTFB) במקרים רבים יצומצם באופן משמעותי, כי הבייט הראשון של התגובה לבקשת ניווט הוא מיידי.
- First Contentful Paint (FCP) יהיה מהיר מאוד, כי תגי העיצוב של הכותרת שנשמרו מראש במטמון מכילים הפניה לגיליון סגנונות שנשמר במטמון. המשמעות היא שהדף ייראה מהר מאוד.
- במקרים מסוימים, גם LCP) יכול לפעול מהר יותר, במיוחד אם הרכיב הגדול ביותר במסך מסופק על ידי החלקית של הכותרת שנשמרה מראש במטמון. למרות זאת, רק הצגה של משהו מתוך המטמון של קובץ השירות (service worker) בהקדם האפשרי במקביל למטענים ייעודיים (payloads) קטנים יותר של תגי עיצוב עשויה להוביל ל-LCP טוב יותר.
יכול להיות קצת מסובך להגדיר ארכיטקטורות סטרימינג של דפים מרובים ולהתנסות בהן, אבל לעיתים קרובות המורכבות הכרוכה הזו לא כבדה יותר מ בבקשה לספקי שירותים (SPA). היתרון העיקרי הוא שאתם לא מחליפים את סכימת הניווט שמוגדרת כברירת מחדל בדפדפן, אלא משפרים אותה.
בנוסף, כשמשתמשים ב-Workbox, הארכיטקטורה הזו לא רק אפשרית, אלא גם קלה יותר מאשר אם הייתם יכולים ליישם אותה בעצמכם. אתם יכולים לנסות אותו באתר שלכם ולראות עד כמה האתר מרובה-הדפים שלכם יכול לפעול מהר יותר עבור המשתמשים בשטח.