प्रोग्रेसिव वेब ऐप्लिकेशन की एक मुख्य खासियत यह है कि ये भरोसेमंद होते हैं. ये ऐसेट को तेज़ी से लोड कर सकते हैं. इससे उपयोगकर्ता जुड़े रहते हैं और उन्हें तुरंत फ़ीडबैक मिलता है. ऐसा तब भी होता है, जब नेटवर्क की स्पीड अच्छी न हो. ऐसा कैसे हो सकता है? यह fetch
इवेंट के लिए सर्विस वर्कर की वजह से होता है.
फ़ेच इवेंट
fetch
इवेंट की मदद से, हम सर्विस वर्कर के स्कोप में PWA के किए गए हर नेटवर्क अनुरोध को इंटरसेप्ट कर सकते हैं. ऐसा एक जैसे ऑरिजिन और अलग-अलग ऑरिजिन, दोनों तरह के अनुरोधों के लिए किया जा सकता है. इंस्टॉल किए गए सर्विस वर्कर से फ़ेच करने पर, नेविगेशन और ऐसेट के अनुरोधों के अलावा, साइट के पहली बार लोड होने के बाद पेज पर आने वाले लोगों को नेटवर्क कॉल के बिना रेंडर किया जा सकता है.
fetch
हैंडलर को किसी ऐप्लिकेशन से मिलने वाले सभी अनुरोध मिलते हैं. इनमें यूआरएल और एचटीटीपी हेडर शामिल हैं. साथ ही, यह ऐप्लिकेशन डेवलपर को यह तय करने की अनुमति देता है कि उन्हें कैसे प्रोसेस किया जाए.
आपका सर्विस वर्कर, अनुरोध को नेटवर्क पर फ़ॉरवर्ड कर सकता है. साथ ही, पहले से कैश किए गए जवाब का इस्तेमाल कर सकता है या नया जवाब बना सकता है. यह आपको तय करना है. इसका एक आसान सा उदाहरण देखें :
self.addEventListener("fetch", event => {
console.log(`URL requested: ${event.request.url}`);
});
अनुरोध का जवाब देना
जब आपके सर्विस वर्कर को कोई अनुरोध मिलता है, तो आपके पास दो विकल्प होते हैं. पहला, अनुरोध को अनदेखा करना. ऐसा करने पर, अनुरोध नेटवर्क पर चला जाता है. दूसरा, अनुरोध का जवाब देना. सर्विस वर्कर से अनुरोधों का जवाब देने की सुविधा की मदद से, यह चुना जा सकता है कि आपके PWA को क्या और कैसे वापस भेजा जाए. ऐसा तब भी किया जा सकता है, जब उपयोगकर्ता ऑफ़लाइन हो.
आने वाले अनुरोध का जवाब देने के लिए, fetch
इवेंट हैंडलर में event.respondWith()
को इस तरह कॉल करें:
// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
const response = .... // a response or a Promise of response
event.respondWith(response);
});
आपको respondWith()
को सिंक्रोनस तरीके से कॉल करना होगा. साथ ही, आपको Response ऑब्जेक्ट दिखाना होगा. हालांकि, फ़ेच इवेंट हैंडलर के खत्म होने के बाद, respondWith()
को कॉल नहीं किया जा सकता. जैसे, एसिंक कॉल के दौरान. अगर आपको पूरे जवाब का इंतज़ार करना है, तो respondWith()
को एक Promise पास करें. यह Promise, Response के साथ रिज़ॉल्व होता है.
जवाब तैयार किए जा रहे हैं
Fetch API की मदद से, JavaScript कोड में एचटीटीपी रिस्पॉन्स बनाए जा सकते हैं. इन रिस्पॉन्स को Cache Storage API का इस्तेमाल करके कैश किया जा सकता है. साथ ही, इन्हें इस तरह से दिखाया जा सकता है जैसे ये किसी वेब सर्वर से आ रहे हों.
जवाब बनाने के लिए, एक नया Response
ऑब्जेक्ट बनाएं. साथ ही, इसके मुख्य हिस्से और स्टेटस और हेडर जैसे विकल्पों को सेट करें:
const simpleResponse = new Response("Body of the HTTP response");
const options = {
status: 200,
headers: {
'Content-type': 'text/html'
}
};
const htmlResponse = new Response("<b>HTML</b> content", options)
कैश मेमोरी से जवाब देना
अब आपको पता चल गया है कि सर्विस वर्कर से एचटीटीपी रिस्पॉन्स कैसे दिखाए जाते हैं. इसलिए, अब समय आ गया है कि डिवाइस पर ऐसेट सेव करने के लिए, कैशिंग स्टोरेज इंटरफ़ेस का इस्तेमाल किया जाए.
कैश स्टोरेज एपीआई का इस्तेमाल करके, यह देखा जा सकता है कि PWA से मिला अनुरोध कैश मेमोरी में मौजूद है या नहीं. अगर मौजूद है, तो respondWith()
को इसका जवाब दिया जा सकता है.
इसके लिए, आपको पहले कैश मेमोरी में खोज करनी होगी. टॉप-लेवल caches
इंटरफ़ेस पर उपलब्ध match()
फ़ंक्शन, आपके ऑरिजिन में मौजूद सभी स्टोर या किसी एक खुले कैश ऑब्जेक्ट को खोजता है.
match()
फ़ंक्शन को आर्ग्युमेंट के तौर पर एचटीटीपी अनुरोध या यूआरएल मिलता है. यह एक प्रॉमिस दिखाता है, जो उस कुंजी से जुड़े जवाब के साथ हल होता है.
// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
// Cache-specific search
caches.open("pwa-assets").then(cache => {
cache.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
});
कैश मेमोरी में सेव करने की रणनीतियां
सिर्फ़ ब्राउज़र की कैश मेमोरी से फ़ाइलें दिखाने की सुविधा, हर इस्तेमाल के उदाहरण के लिए सही नहीं है. उदाहरण के लिए, उपयोगकर्ता या ब्राउज़र कैश मेमोरी को हटा सकता है. इसलिए, आपको अपने PWA के लिए ऐसेट डिलीवर करने की अपनी रणनीतियां तय करनी चाहिए.
आपके पास एक से ज़्यादा कैश मेमोरी की रणनीतियां इस्तेमाल करने का विकल्प होता है. अलग-अलग यूआरएल पैटर्न के लिए, अलग-अलग फ़िल्टर तय किए जा सकते हैं. उदाहरण के लिए, आपके पास कम से कम यूज़र इंटरफ़ेस (यूआई) ऐसेट के लिए एक रणनीति, एपीआई कॉल के लिए दूसरी रणनीति, और इमेज और डेटा यूआरएल के लिए तीसरी रणनीति हो सकती है.
इसके लिए, ServiceWorkerGlobalScope.onfetch
में मौजूद event.request.url
पढ़ें और इसे रेगुलर एक्सप्रेशन या यूआरएल पैटर्न के ज़रिए पार्स करें. (यह लेख लिखते समय, यूआरएल पैटर्न की सुविधा सभी प्लैटफ़ॉर्म पर उपलब्ध नहीं है).
सबसे ज़्यादा इस्तेमाल की जाने वाली रणनीतियां ये हैं:
- कैश फ़र्स्ट
- यह सबसे पहले कैश मेमोरी में सेव किए गए जवाब को खोजता है. अगर कोई जवाब नहीं मिलता है, तो यह नेटवर्क पर वापस चला जाता है.
- नेटवर्क फ़र्स्ट
- यह नेटवर्क से पहले जवाब का अनुरोध करता है. अगर कोई जवाब नहीं मिलता है, तो यह कैश मेमोरी में जवाब ढूंढता है.
- Stale While Revalidate
- यह कैश मेमोरी से जवाब देता है. साथ ही, बैकग्राउंड में नए वर्शन का अनुरोध करता है और उसे कैश मेमोरी में सेव करता है, ताकि अगली बार जब ऐसेट का अनुरोध किया जाए, तो उसका इस्तेमाल किया जा सके.
- Network-Only
- हमेशा नेटवर्क से मिले जवाब के साथ जवाब देता है या गड़बड़ी की जानकारी देता है. कैश मेमोरी का इस्तेमाल कभी नहीं किया जाता.
- Cache-Only
- यह हमेशा कैश मेमोरी से जवाब देता है या गड़बड़ी दिखाता है. नेटवर्क से कभी सलाह नहीं ली जाएगी. इस रणनीति का इस्तेमाल करके दिखाई जाने वाली ऐसेट को, अनुरोध किए जाने से पहले कैश मेमोरी में जोड़ना होगा.
कैशे फ़र्स्ट
इस रणनीति का इस्तेमाल करके, सर्विस वर्कर कैश मेमोरी में मैचिंग अनुरोध ढूंढता है. अगर अनुरोध कैश किया गया है, तो वह उससे जुड़ा रिस्पॉन्स दिखाता है. अगर ऐसा नहीं है, तो यह नेटवर्क से रिस्पॉन्स वापस पाता है. इसके अलावा, यह आने वाले समय में किए जाने वाले कॉल के लिए, कैश मेमोरी को अपडेट भी कर सकता है. अगर कैश मेमोरी से कोई जवाब नहीं मिलता है और न ही नेटवर्क से कोई जवाब मिलता है, तो अनुरोध में गड़बड़ी होगी. नेटवर्क पर जाए बिना ऐसेट दिखाने में कम समय लगता है. इसलिए, यह रणनीति नई ऐसेट के बजाय परफ़ॉर्मेंस को प्राथमिकता देती है.
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
// It can update the cache to serve updated content on the next request
return cachedResponse || fetch(event.request);
}
)
)
});
नेटवर्क फ़र्स्ट
यह रणनीति, Cache First रणनीति की तरह ही काम करती है. यह देखती है कि क्या अनुरोध को नेटवर्क से पूरा किया जा सकता है. अगर ऐसा नहीं किया जा सकता, तो यह उसे कैश मेमोरी से वापस पाने की कोशिश करती है. जैसे, कैश मेमोरी पहले. अगर नेटवर्क से कोई जवाब नहीं मिलता है और न ही कैश मेमोरी से कोई जवाब मिलता है, तो अनुरोध में गड़बड़ी होगी. नेटवर्क से रिस्पॉन्स पाने में, कैश मेमोरी से रिस्पॉन्स पाने की तुलना में ज़्यादा समय लगता है. इसलिए, इस रणनीति में परफ़ॉर्मेंस के बजाय अपडेट किए गए कॉन्टेंट को प्राथमिकता दी जाती है.
self.addEventListener("fetch", event => {
event.respondWith(
fetch(event.request)
.catch(error => {
return caches.match(event.request) ;
})
);
});
Stale while revalidate
'पुराना डेटा दिखाएं और बैकग्राउंड में नया डेटा पाएं' रणनीति के तहत, कैश किया गया जवाब तुरंत दिखाया जाता है. इसके बाद, नेटवर्क पर अपडेट की जांच की जाती है. अगर कोई अपडेट मिलता है, तो कैश किए गए जवाब को बदल दिया जाता है. यह रणनीति हमेशा नेटवर्क अनुरोध करती है. ऐसा इसलिए, क्योंकि अगर कैश मेमोरी में सेव किया गया कोई संसाधन मिल भी जाता है, तो यह रणनीति कैश मेमोरी में मौजूद संसाधन को नेटवर्क से मिले संसाधन से अपडेट करने की कोशिश करेगी, ताकि अगले अनुरोध में अपडेट किए गए वर्शन का इस्तेमाल किया जा सके. इसलिए, यह रणनीति आपको कैश मेमोरी को पहले इस्तेमाल करने की रणनीति का फ़ायदा देती है. साथ ही, बैकग्राउंड में कैश मेमोरी को अपडेट करने की सुविधा भी देती है.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
const networkFetch = fetch(event.request).then(response => {
// update the cache with a clone of the network response
const responseClone = response.clone()
caches.open(url.searchParams.get('name')).then(cache => {
cache.put(event.request, responseClone)
})
return response
}).catch(function (reason) {
console.error('ServiceWorker fetch failed: ', reason)
})
// prioritize cached response over network
return cachedResponse || networkFetch
}
)
)
})
सिर्फ़ नेटवर्क
नेटवर्क से जुड़ी रणनीति, ब्राउज़र के उस व्यवहार से मिलती-जुलती है जो सर्विस वर्कर या Cache Storage API के बिना होता है. अनुरोधों के जवाब में सिर्फ़ तब कोई संसाधन दिखाया जाएगा, जब उसे नेटवर्क से फ़ेच किया जा सकता हो. यह अक्सर उन संसाधनों के लिए काम का होता है जो सिर्फ़ ऑनलाइन उपलब्ध हैं. जैसे, एपीआई के अनुरोध.
सिर्फ़ कैश मेमोरी
सिर्फ़ कैश मेमोरी की रणनीति यह पक्का करती है कि अनुरोध कभी नेटवर्क पर न जाएं. सभी आने वाले अनुरोधों का जवाब, पहले से भरी गई कैश मेमोरी के आइटम से दिया जाता है. यहां दिए गए कोड में, fetch
इवेंट हैंडलर का इस्तेमाल किया गया है. साथ ही, इसमें कैश मेमोरी से ही जवाब पाने के लिए, कैश स्टोरेज के match
तरीके का इस्तेमाल किया गया है:
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request));
});
कस्टम रणनीतियां
ऊपर बताई गई, कैश मेमोरी इस्तेमाल करने की सामान्य रणनीतियां हैं. हालांकि, आपके पास यह तय करने का विकल्प होता है कि सर्विस वर्कर का इस्तेमाल कैसे किया जाए और अनुरोधों को कैसे हैंडल किया जाए. अगर इनमें से कोई भी आपकी ज़रूरतों के मुताबिक नहीं है, तो अपनी ज़रूरत के हिसाब से कोई फ़िल्टर बनाएं.
उदाहरण के लिए, अपडेट किए गए कॉन्टेंट को प्राथमिकता देने के लिए, टाइम आउट के साथ नेटवर्क फ़र्स्ट रणनीति का इस्तेमाल किया जा सकता है. हालांकि, ऐसा सिर्फ़ तब किया जा सकता है, जब जवाब आपके सेट किए गए थ्रेशोल्ड के अंदर दिखता हो. आपके पास कैश मेमोरी में सेव किए गए रिस्पॉन्स को नेटवर्क रिस्पॉन्स के साथ मर्ज करने का विकल्प भी होता है. इससे सर्विस वर्कर से एक मुश्किल रिस्पॉन्स बनाया जा सकता है.
ऐसेट अपडेट की जा रही हैं
अपने PWA की कैश मेमोरी में सेव की गई ऐसेट को अप-टू-डेट रखना मुश्किल हो सकता है. 'रीवैलिडेशन के दौरान पुराना डेटा इस्तेमाल करें' रणनीति, ऐसा करने का एक तरीका है. हालांकि, यह एकमात्र तरीका नहीं है. अपडेट चैप्टर में, आपको अपने ऐप्लिकेशन के कॉन्टेंट और ऐसेट को अपडेट रखने के अलग-अलग तरीके मिलेंगे.