स्ट्रीम के साथ ज़्यादा तेज़ी से कई पेज वाले ऐप्लिकेशन

इन दिनों, वेबसाइटों—या वेब ऐप्लिकेशन, अगर आप चाहें, तो इनमें से किसी एक नेविगेशन स्कीम का इस्तेमाल करते हैं:

  • नेविगेशन स्कीम ब्राउज़र डिफ़ॉल्ट रूप से उपलब्ध कराते हैं—इसका मतलब है कि आपको अपने ब्राउज़र के पता बार में यूआरएल डालना होता है और नेविगेशन अनुरोध जवाब के तौर पर दस्तावेज़ को दिखाता है. इसके बाद, आपको एक लिंक पर क्लिक करना होता है. यह लिंक, ad infinitum में मौजूद एक अन्य लिंक के लिए, मौजूदा दस्तावेज़ को अनलोड कर देता है.
  • सिंगल पेज ऐप्लिकेशन पैटर्न, जिसमें ऐप्लिकेशन शेल लोड करने के लिए शुरुआती नेविगेशन अनुरोध शामिल होता है और हर "नेविगेशन" के लिए बैक-एंड एपीआई से कॉन्टेंट के साथ क्लाइंट-रेंडर किए गए मार्कअप से ऐप्लिकेशन शेल पॉप्युलेट करने के लिए, JavaScript पर निर्भर करता है.

इन तरीकों के फ़ायदे, उनके समर्थकों ने बताए हैं:

  • ब्राउज़र की ओर से डिफ़ॉल्ट रूप से दी गई नेविगेशन स्कीम, अपनी सुविधा के हिसाब से काम करती है. ऐसा इसलिए है, क्योंकि रूट के लिए JavaScript को ऐक्सेस करने की ज़रूरत नहीं होती. JavaScript के ज़रिए मार्कअप को क्लाइंट-रेंडरिंग करना भी काफ़ी महंगा प्रोसेस हो सकता है. इसका मतलब है कि कम सुविधाओं वाले डिवाइसों पर कॉन्टेंट उपलब्ध कराने में देरी हो सकती है, क्योंकि डिवाइस, कॉन्टेंट उपलब्ध कराने वाली स्क्रिप्ट को प्रोसेस नहीं करता है.
  • दूसरी ओर, सिंगल पेज ऐप्लिकेशन (एसपीए) पहली बार लोड होने के बाद तेज़ी से नेविगेशन दे सकते हैं. एक नए दस्तावेज़ के लिए एक दस्तावेज़ को अनलोड करने के लिए ब्राउज़र पर निर्भर रहने के बजाय, वे ज़्यादा तेज़ और ज़्यादा "ऐप्लिकेशन जैसा" अनुभव दे सकते हैं. भले ही, उन्हें इस्तेमाल करने के लिए JavaScript की ज़रूरत हो.

इस पोस्ट में, हम ऐसे तीसरे तरीके के बारे में बात करेंगे जो ऊपर बताए गए दो तरीकों के बीच संतुलन बनाता है: वेबसाइट के सामान्य एलिमेंट, जैसे कि हेडर और फ़ुटर मार्कअप को पहले से कैश मेमोरी में सेव करने के लिए, सर्विस वर्कर पर भरोसा करना. साथ ही, ब्राउज़र की डिफ़ॉल्ट नेविगेशन स्कीम का इस्तेमाल करते हुए क्लाइंट को जल्द से जल्द एचटीएमएल रिस्पॉन्स देने के लिए स्ट्रीम का इस्तेमाल करना.

सर्विस वर्कर में एचटीएमएल वाले जवाबों को स्ट्रीम क्यों करना चाहिए?

आपका वेब ब्राउज़र स्ट्रीमिंग की सुविधा को पहले से ही तब इस्तेमाल करता है, जब वह अनुरोध करता है. नेविगेशन के अनुरोधों के मामले में यह बेहद ज़रूरी है. इससे यह पक्का होता है कि ब्राउज़र पर, दस्तावेज़ के मार्कअप को पार्स करने और पेज को रेंडर करने से पहले, पूरे जवाब का इंतज़ार तो नहीं हो रहा है.

नॉन-स्ट्रीमिंग एचटीएमएल बनाम स्ट्रीमिंग एचटीएमएल को दिखाने वाला डायग्राम. पहले वाले मामले में, पूरा मार्कअप पेलोड तब तक प्रोसेस नहीं होता, जब तक वह उपलब्ध नहीं होता. बाद में, जब मार्कअप नेटवर्क से कई हिस्सों में आता है, तो उसे धीरे-धीरे प्रोसेस किया जाता है.

सर्विस वर्कर के लिए, JavaScript Streams API का इस्तेमाल करने की तरह, स्ट्रीमिंग थोड़ी अलग होती है. सर्विस वर्कर के सबसे ज़रूरी काम, अनुरोधों को रोकना और उनका जवाब देना होता है. इसमें नेविगेशन के अनुरोध भी शामिल हैं.

ये अनुरोध कई तरीकों से कैश के साथ इंटरैक्ट कर सकते हैं, लेकिन मार्कअप को कैश मेमोरी में सेव करने का एक सामान्य पैटर्न यह है कि नेटवर्क पहले से मिले जवाब का इस्तेमाल किया जाए. अगर पुरानी कॉपी उपलब्ध है, तो कैश मेमोरी में वापस चले जाएं. अगर कैश मेमोरी में इस्तेमाल करने लायक जवाब मौजूद नहीं है, तो सामान्य फ़ॉलबैक रिस्पॉन्स दें.

यह मार्कअप के लिए एक टेस्ट किया गया पैटर्न है, जो अच्छी तरह से काम करता है. हालांकि, ऑफ़लाइन ऐक्सेस के मामले में यह भरोसेमंद होने में मदद करता है. हालांकि, इससे ऐसे नेविगेशन अनुरोधों के लिए परफ़ॉर्मेंस में कोई फ़ायदा नहीं मिलता जो सिर्फ़ 'नेटवर्क फ़र्स्ट' या 'सिर्फ़ नेटवर्क' रणनीति का इस्तेमाल करते हैं. यहीं से स्ट्रीमिंग की सुविधा शुरू होती है. अब हम आपके Workbox सर्विस वर्कर में Streams API से चलने वाले workbox-streams मॉड्यूल का इस्तेमाल करने का तरीका जानेंगे, ताकि आपकी कई पेजों वाली वेबसाइट पर नेविगेशन के अनुरोधों में तेज़ी लाई जा सके.

किसी सामान्य वेब पेज के बारे में बताना

अगर देखा जाए, तो वेबसाइटों में एक जैसे एलिमेंट होते हैं, जो हर पेज पर मौजूद होते हैं. पेज एलिमेंट को आम तौर पर कुछ इस तरह से दिखाया जाता है:

  • हेडर.
  • सामग्री.
  • फ़ुटर.

उदाहरण के तौर पर web.dev का इस्तेमाल करके, सामान्य एलिमेंट का ब्रेकडाउन ऐसा दिखता है:

web.dev वेबसाइट पर मौजूद सामान्य एलिमेंट का ब्रेकडाउन. दिखाई गई सामान्य जगहों को 'हेडर', 'कॉन्टेंट', और 'फ़ुटर' के तौर पर मार्क किया जाता है.

किसी पेज के हिस्सों की पहचान करने के पीछे हमारा मकसद यह पता करना होता है कि नेटवर्क पर जाए बिना कौनसी चीज़ें पहले से कैश मेमोरी में सेव की जा सकती हैं और फिर से वापस पाई जा सकती हैं—जैसे कि सभी पेजों के लिए हेडर और फ़ुटर मार्कअप—और पेज का वह हिस्सा जिस पर हम पहले नेटवर्क पर जाते हैं—इस मामले में वह कॉन्टेंट.

जब हमें पता होता है कि पेज के हिस्सों को कैसे बांटा जाए और सामान्य एलिमेंट की पहचान कैसे की जाए, तब हम एक सर्विस वर्कर लिख सकते हैं. हम ऐसा सर्विस वर्कर लिख सकते हैं जो नेटवर्क से सिर्फ़ कॉन्टेंट का अनुरोध करने पर, कैश मेमोरी से हमेशा हेडर और फ़ुटर मार्कअप को तुरंत हासिल कर लेता है.

इसके बाद, workbox-streams के ज़रिए Streams API का इस्तेमाल करके, हम इन सभी हिस्सों को एक साथ जोड़ सकते हैं और नेविगेशन के अनुरोधों का तुरंत जवाब दे सकते हैं. ऐसा करते हुए, हम नेटवर्क से कम से कम ज़रूरी मार्कअप जोड़ने का अनुरोध कर सकते हैं.

स्ट्रीमिंग सर्विस वर्कर बनाना

जब बात किसी सर्विस वर्कर से मिलने वाले कॉन्टेंट की स्ट्रीमिंग की हो, तो बहुत सारी चीज़ें चलती हैं. हालांकि, जैसे-जैसे आपको आगे बढ़ने के बारे में जानकारी मिलती है, पूरी प्रोसेस के हर चरण के बारे में विस्तार से जानकारी दी जाएगी. सबसे पहले हम यह जानेंगे कि आपको अपनी वेबसाइट को कैसे व्यवस्थित करना है.

अपनी वेबसाइट को हिस्सों में बांटना

स्ट्रीमिंग सर्विस वर्कर के लिए ऐप्लिकेशन लिखना शुरू करने से पहले, आपको तीन काम करने होंगे:

  1. सिर्फ़ अपनी वेबसाइट के हेडर मार्कअप वाली फ़ाइल बनाएं.
  2. सिर्फ़ अपनी वेबसाइट के फ़ुटर मार्कअप वाली फ़ाइल बनाएं.
  3. हर पेज का मुख्य कॉन्टेंट एक अलग फ़ाइल में डालें या अपने बैक एंड को इस तरह सेट अप करें कि वह एचटीटीपी अनुरोध के हेडर के आधार पर, सिर्फ़ पेज का कॉन्टेंट कुछ शर्तों के साथ पेश करे.

ऐसा हो सकता है कि आखिरी चरण सबसे मुश्किल हो. खास तौर पर तब, जब आपकी वेबसाइट स्थिर हो. अगर आपके मामले में ऐसा है, तो आपको हर पेज के दो वर्शन जनरेट करने होंगे: एक वर्शन में पूरा पेज मार्कअप होगा, जबकि दूसरे में सिर्फ़ कॉन्टेंट होगा.

स्ट्रीमिंग सर्विस वर्कर को कंपोज़ करना

अगर आपने workbox-streams मॉड्यूल इंस्टॉल नहीं किया है, तो आपको मौजूदा Workbox मॉड्यूल के साथ-साथ ऐसा करना होगा. इस उदाहरण में, ये पैकेज शामिल हैं:

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

यहां से, अगला चरण अपने नए सर्विस वर्कर को बनाना और अपने हेडर और फ़ुटर पार्शियल को प्री-कैश करना होता है.

प्रीकैशिंग पार्शियल

सबसे पहले आपको अपने प्रोजेक्ट के रूट में 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...

यह कोड कुछ काम करता है:

  1. इसका इस्तेमाल करने वाले ब्राउज़र के लिए, नेविगेशन प्रीलोड को चालू करता है.
  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.

इस कोड के तीन मुख्य हिस्से होते हैं, जो यहां दी गई ज़रूरी शर्तों को पूरा करते हैं:

  1. NetworkFirst रणनीति का इस्तेमाल, कॉन्टेंट के कुछ हिस्सों के अनुरोधों को मैनेज करने के लिए किया जाता है. इस रणनीति का इस्तेमाल करके, content का कस्टम कैश नाम दिया गया है, ताकि इसमें कॉन्टेंट के पार्शियल को शामिल किया जा सके. साथ ही, यह कस्टम प्लगिन भी इस्तेमाल किया जा सकता है. यह प्लगिन यह मैनेज करता है कि जिन ब्राउज़र में नेविगेशन प्रीलोड की सुविधा काम नहीं करती, उनमें X-Content-Mode अनुरोध का हेडर सेट किया जाए या नहीं. इसलिए, Service-Worker-Navigation-Preload हेडर न भेजें. यह प्लगिन यह भी पता लगा लेता है कि किसी कॉन्टेंट का, कैश मेमोरी में सेव किया गया आखिरी वर्शन सेव नहीं किया जाना चाहिए या मौजूदा अनुरोध के लिए कैश मेमोरी में सेव किया गया कोई वर्शन सेव न होने की स्थिति में, इस पेज को ऑफ़लाइन भेजना है या नहीं.
  2. workbox-streams में strategy तरीके (इसे यहां composeStrategies के तौर पर बताया गया है) का इस्तेमाल, पहले से कैश मेमोरी में सेव किए गए हेडर और फ़ुटर पार्शियल को नेटवर्क से अनुरोध किए गए कॉन्टेंट के साथ जोड़ने के लिए किया जाता है.
  3. नेविगेशन के अनुरोधों के लिए, पूरी स्कीम को registerRoute से जोड़ा गया है.

इस लॉजिक को लागू करने के बाद, हमने जवाबों को स्ट्रीम करने की सुविधा सेट अप की है. हालांकि, आपको बैक एंड पर कुछ काम करना पड़ सकता है, ताकि यह पक्का किया जा सके कि नेटवर्क से कॉन्टेंट का सिर्फ़ एक हिस्सा शामिल हो, जिसे पहले से कैश मेमोरी में सेव किए गए पार्शियल के साथ मर्ज किया जा सके.

अगर आपकी वेबसाइट का बैक एंड है

आपको याद होगा कि नेविगेशन को पहले से लोड करने की सुविधा चालू होने पर, ब्राउज़र true वैल्यू के साथ एक Service-Worker-Navigation-Preload हेडर भेजता है. हालांकि, ऊपर दिए गए कोड सैंपल में, हमने इवेंट नेविगेशन प्रीलोड में 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 रेंडरर फ़ंक्शन, आंशिक रूप से मिली शर्तों में सिर्फ़ कुछ मार्कअप को शामिल करे—इसे जल्द ही शामिल किया जाएगा.

इन बातों पर ध्यान दें

स्ट्रीम करने के लिए किसी सर्विस वर्कर को डिप्लॉय करने से पहले, कुछ चीज़ों को एक साथ स्टिच करें. इसके लिए, आपको कुछ बातों का ध्यान रखना होगा. हालांकि यह सच है कि किसी सर्विस वर्कर का इस तरह से इस्तेमाल करने से ब्राउज़र का डिफ़ॉल्ट नेविगेशन व्यवहार बुनियादी तौर पर नहीं बदलता, लेकिन कुछ ऐसी चीज़ें हैं जिन्हें आपको ठीक करना पड़ सकता है.

नेविगेट करते समय, पेज के एलिमेंट अपडेट करना

इस तरीके का सबसे मुश्किल हिस्सा यह है कि आपको क्लाइंट पर कुछ चीज़ों को अपडेट करना होगा. उदाहरण के लिए, हेडर मार्कअप को पहले से कैश मेमोरी में सेव करने का मतलब है कि पेज के <title> एलिमेंट में एक जैसा कॉन्टेंट होगा. इसके अलावा, हर नेविगेशन पर, नेविगेशन आइटम के लिए चालू/बंद की स्थिति को मैनेज करने की स्थिति को भी अपडेट करना होगा. इन चीज़ों और दूसरी चीज़ों को हर नेविगेशन अनुरोध के लिए क्लाइंट पर अपडेट करना पड़ सकता है.

इससे बचने के लिए, यह हो सकता है कि कुछ ज़रूरी चीज़ों को अपडेट करने के लिए, नेटवर्क से मिलने वाले कॉन्टेंट के कुछ हिस्से में इनलाइन <script> एलिमेंट डाला जाए:

<!-- 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>

यह सिर्फ़ एक उदाहरण है कि अगर आपको इस सर्विस वर्कर सेटअप के साथ आगे बढ़ना है, तो आपको क्या करना होगा. उदाहरण के लिए, उपयोगकर्ता की जानकारी वाले ज़्यादा जटिल ऐप्लिकेशन के लिए, आपको localStorage जैसे किसी वेब स्टोर में काम के डेटा के कुछ हिस्से स्टोर करने पड़ सकते हैं और वहां से पेज अपडेट करना पड़ सकता है.

धीमे नेटवर्क से निपटना

प्रीकैश मेमोरी से मार्कअप का इस्तेमाल करके जवाबों को स्ट्रीम करने का एक नुकसान यह हो सकता है कि इंटरनेट कनेक्शन धीमा होने पर, उसे स्ट्रीम किया जा सके. समस्या यह है कि प्रीकैश से हेडर मार्कअप तुरंत मिल जाएगा, लेकिन हेडर मार्कअप के शुरुआती पेंट के बाद, नेटवर्क से कुछ कॉन्टेंट को पहुंचने में कुछ समय लग सकता है.

इससे भ्रम की स्थिति पैदा हो सकती है. अगर नेटवर्क बहुत धीमा है, तो आपको यह भी लग सकता है कि पेज काम नहीं कर रहा है और वह अब रेंडर नहीं हो रहा है. इस तरह के मामलों में, कॉन्टेंट के कुछ हिस्से के मार्कअप में लोड होने वाला आइकॉन या मैसेज जोड़ने का विकल्प चुना जा सकता है. कॉन्टेंट लोड होने के बाद, इसे छिपाया जा सकता है.

ऐसा करने का एक तरीका सीएसएस की मदद से है. मान लें कि आपके हेडर का कुछ हिस्सा, शुरुआती <article> एलिमेंट के साथ खत्म होता है. यह एलिमेंट तब तक खाली रहता है, जब तक कि कॉन्टेंट का कुछ हिस्सा न दिखे. आप इससे मिलता-जुलता CSS नियम लिख सकते हैं:

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

यह काम कर लेता है, लेकिन यह क्लाइंट को लोड होने का मैसेज दिखाएगा, भले ही नेटवर्क की स्पीड कुछ भी हो. अगर आपको अचानक से मैसेज भेजने से बचना है, तो यह तरीका आज़माएं. यहां हम slow क्लास में, ऊपर दिए गए स्निपेट में सिलेक्टर को नेस्ट करते हैं:

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

यहां से, आपके पास हेडर के कुछ हिस्से में JavaScript का इस्तेमाल करने का विकल्प है. इससे, कनेक्शन के चुनिंदा टाइप पर, <html> एलिमेंट में slow क्लास जोड़ने के लिए, असरदार कनेक्शन टाइप (कम से कम Chromium ब्राउज़र में) को पढ़ा जा सकता है:

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

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

इससे यह पक्का होगा कि 4g की तुलना में धीमे काम करने वाले कनेक्शन टाइप को लोड होने का मैसेज मिलेगा. इसके बाद, कॉन्टेंट के कुछ हिस्से में, इनलाइन <script> एलिमेंट डाले जा सकते हैं. ऐसा करके, लोड होने वाले मैसेज को हटाने के लिए, एचटीएमएल से slow क्लास हटाई जा सकती है:

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

फ़ॉलबैक जवाब देना

मान लें कि कॉन्टेंट के कुछ हिस्से के लिए, नेटवर्क को प्राथमिकता देने वाली रणनीति का इस्तेमाल किया जा रहा है. अगर उपयोगकर्ता ऑफ़लाइन है और किसी ऐसे पेज पर जाता है जिस पर वह पहले ही जा चुका है, तो उसे इस सूची में शामिल किया जाएगा. हालांकि, अगर वे ऐसे पेज पर जाते हैं जहां वे अब तक नहीं गए हैं, तो उन्हें कुछ भी नहीं मिलेगा. इससे बचने के लिए, आपको फ़ॉलबैक रिस्पॉन्स देना होगा.

फ़ॉलबैक रिस्पॉन्स पाने के लिए ज़रूरी कोड को पिछले कोड सैंपल में दिखाया जाता है. इस प्रोसेस में दो चरण होते हैं:

  1. ऑफ़लाइन फ़ॉलबैक रिस्पॉन्स को पहले से कैश मेमोरी में सेव करें.
  2. नेटवर्क को पहले इस्तेमाल करने की रणनीति के लिए, प्लगिन में handlerDidError कॉलबैक सेट अप करें, ताकि किसी पेज के आखिरी बार ऐक्सेस किए गए वर्शन की कैश मेमोरी की जांच की जा सके. अगर पेज को कभी ऐक्सेस नहीं किया गया था, तो आपको प्रीकैश से फ़ॉलबैक रिस्पॉन्स पाने के लिए, workbox-precaching मॉड्यूल से matchPrecache तरीके का इस्तेमाल करना होगा.

कैश मेमोरी में सेव करना और सीडीएन

अगर अपने सर्विस वर्कर में इस स्ट्रीमिंग पैटर्न का इस्तेमाल किया जा रहा है, तो देखें कि क्या आपकी स्थिति में ये बातें लागू होती हैं:

  • सीडीएन या किसी दूसरी तरह की इंटरमीडिएट/सार्वजनिक कैश मेमोरी का इस्तेमाल किया जाता है.
  • आपने public डायरेक्टिव के साथ, नॉन-ज़ीरो max-age और/या s-maxage डायरेक्टिव के साथ, Cache-Control हेडर की जानकारी दी है.

अगर आपके लिए ये दोनों बातें लागू होती हैं, तो इंटरमीडिएट कैश मेमोरी, नेविगेशन अनुरोधों के जवाब रोक सकती है. हालांकि, याद रखें कि जब आप इस पैटर्न का इस्तेमाल करते हैं, तब हो सकता है कि किसी दिए गए यूआरएल के लिए आप दो अलग-अलग जवाब दे रहे हों:

  • पूरा जवाब, जिसमें हेडर, कॉन्टेंट, और फ़ुटर मार्कअप शामिल होता है.
  • अधूरे जवाब, जिसमें सिर्फ़ कॉन्टेंट शामिल है.

इसकी वजह से कुछ अनचाहे व्यवहार हो सकते हैं, जिनकी वजह से हेडर और फ़ुटर मार्कअप दो बार हो सकते हैं. ऐसा इसलिए हो सकता है, क्योंकि हो सकता है कि सर्विस वर्कर, सीडीएन कैश से पूरा रिस्पॉन्स फ़ेच कर रहा हो और उसे आपके पहले से कैश मेमोरी में सेव किए गए हेडर और फ़ुटर मार्कअप के साथ मिला रहा हो.

इससे बचने के लिए, आपको Vary हेडर पर भरोसा करना होगा. इससे, अनुरोध में मौजूद एक या एक से ज़्यादा हेडर के कैश मेमोरी में सेव किए जा सकने वाले जवाबों को कुंजी के ज़रिए, कैश मेमोरी में सेव करने के तरीके पर असर पड़ता है. हम Service-Worker-Navigation-Preload और कस्टम X-Content-Mode अनुरोध हेडर के आधार पर नेविगेशन अनुरोधों के जवाबों में बदलाव कर रहे हैं. इसलिए, हमें जवाब में इस Vary हेडर की जानकारी देनी होगी:

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

इस हेडर की मदद से ब्राउज़र, नेविगेशन अनुरोधों के लिए पूरे और आंशिक रूप से दिए गए रिस्पॉन्स के बीच अंतर करेगा. साथ ही, दोगुने हेडर और फ़ुटर मार्कअप की समस्याओं से बचने के लिए, किसी भी इंटरमीडिएट कैश मेमोरी की तरह काम करेगा.

नतीजा

ज़्यादातर मामलों में, पेज लोड होने में लगने वाले समय की परफ़ॉर्मेंस से जुड़ी सलाह का मतलब है, "आपको जो मिला उसे दिखाएं".

जेक आर्किबाल्ड ने ज़्यादा तेज़ कॉन्टेंट के लिए मज़ेदार तरीके बनाए

जब नेविगेशन के अनुरोधों के जवाब देने की बात आती है, तो ब्राउज़र बेहतरीन होते हैं. यहां तक कि बड़े-बड़े एचटीएमएल रिस्पॉन्स बॉडी के लिए भी ब्राउज़र बेहतरीन काम करते हैं. डिफ़ॉल्ट रूप से, ब्राउज़र मार्कअप को धीरे-धीरे कई हिस्सों में स्ट्रीम और प्रोसेस करते हैं. इससे लंबे टास्क करने से बचा जा सकता है. इससे स्टार्टअप की परफ़ॉर्मेंस बेहतर होती है.

जब हम स्ट्रीमिंग सर्विस वर्कर पैटर्न का इस्तेमाल करते हैं, तब यह हमारे लिए फ़ायदेमंद साबित होता है. जब कभी भी, शुरुआत में ही सर्विस वर्कर कैश मेमोरी के अनुरोध का जवाब दिया जाता है, तो जवाब तुरंत शुरू हो जाता है. जब नेटवर्क से मिलने वाले रिस्पॉन्स के साथ, पहले से कैश मेमोरी में सेव किए गए हेडर और फ़ुटर मार्कअप को एक साथ जोड़ा जाता है, तब आपको परफ़ॉर्मेंस से जुड़े कुछ अहम फ़ायदे मिलते हैं:

  • फ़र्स्ट बाइट में लगने वाला समय (TTFB) को अक्सर बहुत कम कर दिया जाएगा, क्योंकि नेविगेशन के अनुरोध का पहला बाइट तुरंत होता है.
  • फ़र्स्ट कॉन्टेंटफ़ुल पेंट (एफ़सीपी) बहुत तेज़ी से काम करेगा, क्योंकि पहले से कैश मेमोरी में सेव किए गए हेडर मार्कअप में, कैश मेमोरी में सेव की गई स्टाइल शीट का रेफ़रंस होगा. इसका मतलब है कि पेज बहुत तेज़ी से पेंट होगा.
  • कुछ मामलों में, सबसे बड़े कॉन्टेंटफ़ुल पेंट (एलसीपी) को भी तेज़ी से लोड किया जा सकता है. खास तौर पर तब, जब स्क्रीन पर सबसे बड़ा एलिमेंट पहले से कैश मेमोरी में सेव किए गए हेडर के पार्शियल के ज़रिए दिया गया हो. फिर भी, छोटे मार्कअप पेलोड के साथ जल्द से जल्द सर्विस वर्कर की कैश मेमोरी से कुछ दिखाने से, एलसीपी बेहतर हो सकता है.

कई पेज वाले आर्किटेक्चर को स्ट्रीम करना, सेट अप करना और उसे दोहराना थोड़ा मुश्किल हो सकता है. हालांकि, इसमें जो मुश्किल काम है वह एसपीए के सिद्धांत से ज़्यादा मुश्किल नहीं है. इसका मुख्य फ़ायदा यह है कि आप ब्राउज़र की डिफ़ॉल्ट नेविगेशन स्कीम को नहीं बदल रहे हैं. बल्कि इसे बेहतर बना रहे हैं.

अब तक, वर्कबॉक्स इस आर्किटेक्चर को न सिर्फ़ संभव बनाता है, बल्कि इसे खुद लागू करने से ज़्यादा आसान बनाता है. इसे अपनी वेबसाइट पर आज़माएं और देखें कि कई पेजों वाली आपकी वेबसाइट, फ़ील्ड के उपयोगकर्ताओं के लिए कितनी तेज़ी से काम कर सकती है.

संसाधन