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

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

Browser Support

  • Chrome: 59.
  • Edge: 18.
  • Firefox: 99.
  • Safari: 15.4.

Source

खास जानकारी

समस्या

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

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

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

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

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

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

Facebook ने हमें इस समस्या के बारे में बताया. साथ ही, उसने नेविगेशन के अनुरोधों को एक साथ पूरा करने का तरीका पूछा:

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

नेविगेशन प्रीलोड की मदद से

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

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

यहां इस सुविधा का एक वीडियो दिया गया है. इसमें, while-लूप का इस्तेमाल करके, सर्विस वर्कर को जान-बूझकर 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
});

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