नेविगेशन पहले से लोड होने की वजह से सर्विस वर्कर की रफ़्तार बढ़ाना

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

ब्राउज़र सहायता

  • Chrome: 59. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • एज: 18. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • Firefox: 99. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • Safari: 15.4. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है

सोर्स

खास जानकारी

समस्या

जब किसी ऐसी साइट पर विज़िट किया जाता है जो फ़ेच इवेंट मैनेज करने के लिए सर्विस वर्कर का इस्तेमाल करती है, तब ब्राउज़र सर्विस वर्कर से जवाब मांगता है. इसमें सर्विस वर्कर को चालू करना (अगर यह पहले से नहीं चल रहा हो) और फ़ेच इवेंट को भेजना शामिल है.

डिवाइस चालू होने में लगने वाला समय, डिवाइस और उसकी स्थितियों के हिसाब से तय होता है. आम तौर पर, यह समय 50 मि॰से॰ का होता है. मोबाइल पर, यह 250 मि॰से॰ से ज़्यादा हो जाता है. बहुत ज़्यादा मामलों में (धीमे डिवाइस, सीपीयू में गड़बड़ी), यह 500 मि॰से॰ से ज़्यादा हो सकता है. हालांकि, सर्विस वर्कर इवेंट के बीच ब्राउज़र के तय समय तक चालू रहता है. इसलिए, आपको यह देरी कभी-कभी ही मिलती है. उदाहरण के लिए, जब कोई उपयोगकर्ता आपकी साइट पर किसी नए टैब या किसी दूसरी साइट से नेविगेट करता है.

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

एसडब्ल्यू बूट
नेविगेशन का अनुरोध

सर्विस वर्कर के चालू होने की वजह से, नेटवर्क अनुरोध में देरी हो रही है.

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

Facebook ने इस समस्या की ओर हमारा ध्यान खींचा और साथ ही साथ नेविगेशन अनुरोध करने का तरीका पूछा:

एसडब्ल्यू बूट
नेविगेशन का अनुरोध

बचाव के लिए नेविगेशन प्रीलोड

नेविगेशन पहले से लोड करने की सुविधा ऐसी सुविधा है जिसकी मदद से आप, "जब उपयोगकर्ता कोई GET नेविगेशन अनुरोध करता है, तब सर्विस वर्कर के चालू होने के दौरान नेटवर्क के लिए अनुरोध शुरू करना" कहते हैं.

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

यहां इसका एक वीडियो दिखाया गया है, जिसमें सर्विस वर्कर को टाइम-लूप का इस्तेमाल करके, 500 मि॰से॰ की स्टार्टअप देरी दी गई है:

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

नेविगेशन पहले से लोड करने की सुविधा चालू करें

addEventListener('activate', event => {
  event.waitUntil(async function() {
    // Feature-detect
    if (self.registration.navigationPreload) {
      // Enable navigation preloads!
      await self.registration.navigationPreload.enable();
    }
  }());
});

आप जब चाहें, navigationPreload.enable() को कॉल कर सकते हैं या navigationPreload.disable() की मदद से इसे बंद कर सकते हैं. हालांकि, आपको अपने fetch इवेंट का इस्तेमाल करना है, इसलिए बेहतर होगा कि अपने सर्विस वर्कर के activate इवेंट में इसे चालू और बंद करें.

पहले से लोड किए गए जवाब का इस्तेमाल करना

अब ब्राउज़र, नेविगेशन के लिए पहले से लोड किए गए डेटा को लोड करेगा, लेकिन आपको अब भी रिस्पॉन्स का इस्तेमाल करना होगा:

addEventListener('fetch', event => {
  event.respondWith(async function() {
    // Respond from the cache if we can
    const cachedResponse = await caches.match(event.request);
    if (cachedResponse) return cachedResponse;

    // Else, use the preloaded response, if it's there
    const response = await event.preloadResponse;
    if (response) return response;

    // Else try the network.
    return fetch(event.request);
  }());
});

event.preloadResponse एक ऐसा प्रॉमिस है जो जवाब के साथ रिज़ॉल्व हो जाता है, अगर:

  • नेविगेशन प्रीलोड चालू है.
  • यह अनुरोध, GET का है.
  • अनुरोध एक नेविगेशन अनुरोध है (जो ब्राउज़र, पेज लोड करते समय जनरेट करते हैं, जिनमें iframe भी शामिल हैं).

अगर ऐसा नहीं है, तो event.preloadResponse अब भी मौजूद है, लेकिन undefined से इसका समाधान हो जाता है.

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

मान लें कि हम एक लेख दिखाना चाहते थे:

addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  const includeURL = new URL(url);
  includeURL.pathname += 'include';

  if (isArticleURL(url)) {
    event.respondWith(async function() {
      // We're going to build a single request from multiple parts.
      const parts = [
        // The top of the page.
        caches.match('/article-top.include'),
        // The primary content
        fetch(includeURL)
          // A fallback if the network fails.
          .catch(() => caches.match('/article-offline.include')),
        // The bottom of the page
        caches.match('/article-bottom.include')
      ];

      // Merge them all together.
      const {done, response} = await mergeResponses(parts);

      // Wait until the stream is complete.
      event.waitUntil(done);

      // Return the merged response.
      return response;
    }());
  }
});

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

यह "ऐप्लिकेशन शेल" से ज़्यादा तेज़ है नेटवर्क अनुरोध के साथ-साथ पेज अनुरोध के साथ किया जाता है. साथ ही, कॉन्टेंट को मुख्य हैक के बिना स्ट्रीम किया जा सकता है.

हालांकि, सर्विस वर्कर के स्टार्टअप समय से includeURL के अनुरोध में देरी होगी. हम इसे ठीक करने के लिए भी नेविगेशन प्रीलोड का इस्तेमाल कर सकते हैं. हालांकि, इस स्थिति में हम पूरे पेज को पहले से लोड नहीं करना चाहते, तो हम शामिल किए गए किसी पेज को पहले से लोड करना चाहते हैं.

यह काम करने के लिए, पेजों को पहले से लोड करने के हर अनुरोध के साथ एक हेडर भेजा जाता है:

Service-Worker-Navigation-Preload: true

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

अब हम पेजों को पहले से लोड करने के अनुरोध का इस्तेमाल कर सकते हैं:

// Try to use the preload
const networkContent = Promise.resolve(event.preloadResponse)
  // Else do a normal fetch
  .then(r => r || fetch(includeURL))
  // A fallback if the network fails.
  .catch(() => caches.match('/article-offline.include'));

const parts = [
  caches.match('/article-top.include'),
  networkContent,
  caches.match('/article-bottom')
];

हेडर बदलना

डिफ़ॉल्ट रूप से, Service-Worker-Navigation-Preload हेडर की वैल्यू true होती है. हालांकि, इसे अपने हिसाब से सेट किया जा सकता है:

navigator.serviceWorker.ready.then(registration => {
  return registration.navigationPreload.setHeaderValue(newValue);
}).then(() => {
  console.log('Done!');
});

उदाहरण के लिए, उसे उस आखिरी पोस्ट के आईडी पर सेट किया जा सकता है जिसे आपने स्थानीय तौर पर कैश मेमोरी में सेव किया है, ताकि सर्वर सिर्फ़ नया डेटा दिखाए.

राज्य की जानकारी पाना

getState का इस्तेमाल करके, पहले से लोड किए गए नेविगेशन की स्थिति देखी जा सकती है:

navigator.serviceWorker.ready.then(registration => {
  return registration.navigationPreload.getState();
}).then(state => {
  console.log(state.enabled); // boolean
  console.log(state.headerValue); // string
});

इस सुविधा पर काम करने और इस लेख में मदद करने के लिए, मैट फ़ाल्कनहेगन और सुयोशी होरो को बहुत-बहुत धन्यवाद. मानक तय करने की कोशिश में शामिल सभी लोगों को बहुत-बहुत धन्यवाद