इंपेरेटिव कैशिंग गाइड

Andrew Guan
Andrew Guan

कुछ वेबसाइटों को नतीजे के बारे में जानकारी दिए बिना, सेवा वर्कर से संपर्क करना पड़ सकता है. यहां कुछ उदाहरण दिए गए हैं:

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

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

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

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

प्रोडक्शन केस

1-800-flower.com ने postMessage() के ज़रिए सर्विस वर्कर के साथ ज़रूरी कैश मेमोरी (प्रीफ़ेचिंग) लागू की. इसका मकसद, कैटगरी वाले पेजों के टॉप आइटम प्रीफ़ेच करना था, ताकि प्रॉडक्ट की जानकारी वाले पेजों पर तेज़ी से नेविगेट किया जा सके.

1 से 800 फूलों का लोगो.

वे यह तय करने के लिए अलग-अलग तरीकों का इस्तेमाल करते हैं कि किन आइटम को पहले से लोड करना है:

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

ये JSON रिस्पॉन्स को सेव करने के लिए, Cache API का इस्तेमाल करते हैं:

1 से 800 फूलों का लोगो.
1-800Flowers.com के प्रॉडक्ट लिस्टिंग पेजों से, प्रॉडक्ट का JSON डेटा पहले से लोड किया जा रहा है.

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

Workbox का इस्तेमाल करना

वर्कबॉक्स, सर्विस वर्कर को workbox-window पैकेज की मदद से मैसेज भेजने का एक आसान तरीका मुहैया कराता है. यह मॉड्यूल का एक सेट होता है, जो विंडो में चलने के लिए बनाया जाता है. ये, सेवा वर्कर में चलने वाले अन्य Workbox पैकेज के साथ काम करते हैं.

पेज को सर्विस वर्कर से जोड़ने के लिए, सबसे पहले रजिस्टर किए गए सर्विस वर्कर का Workbox ऑब्जेक्ट रेफ़रंस पाएं:

const wb = new Workbox('/sw.js');
wb.register();

इसके बाद, सीधे तौर पर मैसेज भेजा जा सकता है. इसके लिए, आपको रजिस्टर करने, चालू होने की जांच करने या कम्यूनिकेशन एपीआई के बारे में सोचने की ज़रूरत नहीं है:

wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });

इन मैसेज को सुनने के लिए, सेवा वर्कर एक message हैंडलर लागू करता है. यह विकल्प के तौर पर जवाब दिखा सकता है. हालांकि, इन मामलों में ऐसा करना ज़रूरी नहीं है:

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PREFETCH') {
    // do something
  }
});

ब्राउज़र एपीआई का इस्तेमाल करना

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

postMessage API का इस्तेमाल, पेज से सेवा वर्कर के बीच एकतरफ़ा कम्यूनिकेशन मैकेनिज़्म बनाने के लिए किया जा सकता है.

पेज, सर्विस वर्कर इंटरफ़ेस पर postMessage() को कॉल करता है:

navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
  payload: 'some data to perform the task',
});

इन मैसेज को सुनने के लिए, सेवा वर्कर एक message हैंडलर लागू करता है.

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === MSG_ID) {
    // do something
  }
});

{type : 'MSG_ID'} एट्रिब्यूट का इस्तेमाल करना ज़रूरी नहीं है. हालांकि, यह पेज को सर्विस वर्कर को अलग-अलग तरह के निर्देश भेजने की अनुमति देने का एक तरीका है. जैसे, 'प्रीफ़ेच करने के लिए' बनाम 'स्टोरेज खाली करने के लिए'. सर्विस वर्कर, इस फ़्लैग के आधार पर अलग-अलग एक्ज़ीक्यूशन पाथ में बंट सकता है.

अगर कार्रवाई पूरी हो जाती है, तो उपयोगकर्ता को इसका फ़ायदा मिलेगा. हालांकि, अगर ऐसा नहीं होता है, तो मुख्य उपयोगकर्ता फ़्लो में कोई बदलाव नहीं होगा. उदाहरण के लिए, जब 1-800-Flowers.com, कॉन्टेंट को पहले से कैश मेमोरी में सेव करने की कोशिश करता है, तो पेज को यह जानने की ज़रूरत नहीं होती कि सेवा वर्कर ने कॉन्टेंट को सेव किया है या नहीं. अगर ऐसा होता है, तो उपयोगकर्ता को तेज़ नेविगेशन का आनंद मिलेगा. अगर ऐसा नहीं होता है, तो पेज को नए पेज पर नेविगेट करना होगा. इसमें थोड़ा ज़्यादा समय लगेगा.

कॉन्टेंट को पहले से लोड करने का एक आसान उदाहरण

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

साइटों में प्रीफ़ेच करने की सुविधा को लागू करने के अलग-अलग तरीके हैं:

दस्तावेज़ों या खास एसेट (JS, CSS वगैरह) को पहले से लोड करने जैसी आसान स्थितियों के लिए, ये तकनीकें सबसे सही हैं.

अगर ज़्यादा लॉजिक की ज़रूरत है, तो इस टास्क को पूरी तरह से सेवा वर्कर को सौंपना बेहतर होता है. उदाहरण के लिए, अपने इंटरनल यूआरएल फ़ेच करने के लिए, पहले से लोड किए गए संसाधन (JSON फ़ाइल या पेज) को पार्स करना.

सर्विस वर्कर को इस तरह की कार्रवाइयां सौंपने के ये फ़ायदे हैं:

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

प्रॉडक्ट की जानकारी वाले पेजों को पहले से लोड करना

सबसे पहले, सेवा वर्कर इंटरफ़ेस पर postMessage() का इस्तेमाल करें और कैश मेमोरी में सेव करने के लिए यूआरएल की एक कलेक्शन पास करें:

navigator.serviceWorker.controller.postMessage({
  type: 'PREFETCH',
  payload: {
    urls: [
      'www.exmaple.com/apis/data_1.json',
      'www.exmaple.com/apis/data_2.json',
    ],
  },
});

सर्विस वर्कर में, किसी भी चालू टैब से भेजे गए मैसेज को इंटरसेप्ट और प्रोसेस करने के लिए, message हैंडलर लागू करें:

addEventListener('message', (event) => {
  let data = event.data;
  if (data && data.type === 'PREFETCH') {
    let urls = data.payload.urls;
    for (let i in urls) {
      fetchAsync(urls[i]);
    }
  }
});

पिछले कोड में, हमने यूआरएल के ऐरे पर बार-बार इस्तेमाल करने के लिए, fetchAsync() नाम का एक छोटा हेल्पर फ़ंक्शन पेश किया था. साथ ही, हमने हर यूआरएल के लिए फ़ेच करने का अनुरोध भी किया था:

async function fetchAsync(url) {
  // await response of fetch call
  let prefetched = await fetch(url);
  // (optionally) cache resources in the service worker storage
}

रिस्पॉन्स मिलने के बाद, रिसॉर्स के कैश मेमोरी में सेव किए गए हेडर का इस्तेमाल किया जा सकता है. हालांकि, कई मामलों में संसाधनों को कैश मेमोरी में सेव नहीं किया जाता. जैसे, प्रॉडक्ट की जानकारी वाले पेजों पर. इसका मतलब है कि उनमें no-cache का Cache-control हेडर होता है. ऐसे मामलों में, फ़ेच किए गए संसाधन को सेवा वर्कर कैश मेमोरी में सेव करके, इस व्यवहार को बदला जा सकता है. इससे फ़ाइल को ऑफ़लाइन दिखाने की सुविधा भी मिलती है.

JSON डेटा के अलावा अन्य

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

मान लें कि हमारे उदाहरण में, दिखाया गया JSON डेटा, किराने की खरीदारी वाली साइट की जानकारी है:

{
  "productName": "banana",
  "productPic": "https://cdn.example.com/product_images/banana.jpeg",
  "unitPrice": "1.99"
 }

प्रॉडक्ट की सूची को दोहराने और हर प्रॉडक्ट के लिए हीरो इमेज को कैश मेमोरी में सेव करने के लिए, fetchAsync() कोड में बदलाव करें:

async function fetchAsync(url, postProcess) {
  // await response of fetch call
  let prefetched = await fetch(url);

  //(optionally) cache resource in the service worker cache

  // carry out the post fetch process if supplied
  if (postProcess) {
    await postProcess(prefetched);
  }
}

async function postProcess(prefetched) {
  let productJson = await prefetched.json();
  if (productJson && productJson.product_pic) {
    fetchAsync(productJson.product_pic);
  }
}

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

नतीजा

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

पेज और सेवा वर्कर के बीच कम्यूनिकेशन के ज़्यादा पैटर्न के लिए, ये देखें:

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