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

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

जेक आर्चिबाल्ड
जेक आर्चिबाल्ड

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

  • 59
  • 18
  • 99
  • 15.4

सोर्स

खास जानकारी

समस्या

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

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

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

SW बूट
नेविगेशन अनुरोध

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

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

Facebook ने इस समस्या के असर की जानकारी हमें दी और इसके साथ-साथ नेविगेशन अनुरोध करने का एक तरीका मांगा:

SW बूट
नेविगेशन अनुरोध



और हमने कहा, "हां, ठीक लग रहा है".

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

नेविगेशन प्रीलोड एक ऐसी सुविधा है, जिसकी मदद से आप कह सकते हैं, "जब उपयोगकर्ता 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
});

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

यह नई इंटरऑपरेबल सीरीज़ का हिस्सा है