सर्विस वर्कर पर माइग्रेट करें

सर्विस वर्कर की मदद से, बैकग्राउंड या इवेंट पेजों को बदलना

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

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

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

बैकग्राउंड स्क्रिप्ट और एक्सटेंशन सर्विस वर्कर के बीच अंतर

कुछ मामलों में, आपको एक्सटेंशन के सर्विस वर्कर दिखेंगे. इन्हें 'बैकग्राउंड स्क्रिप्ट' कहा जाता है. एक्सटेंशन के सर्विस वर्कर, बैकग्राउंड में काम करते हैं. हालांकि, इन्हें बैकग्राउंड स्क्रिप्ट कहना कुछ हद तक गुमराह करने वाला है. ऐसा इसलिए, क्योंकि इससे यह पता चलता है कि दोनों की क्षमताएं एक जैसी हैं. इनके बीच के अंतर के बारे में यहां बताया गया है.

बैकग्राउंड पेजों में हुए बदलाव

सर्विस वर्कर और बैकग्राउंड पेजों में कई अंतर होते हैं.

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

आपको कौनसे बदलाव करने होंगे

बैकग्राउंड स्क्रिप्ट और सर्विस वर्कर के काम करने के तरीके में अंतर होता है. इसलिए, आपको कोड में कुछ बदलाव करने होंगे. सबसे पहले, मेनिफ़ेस्ट फ़ाइल में सर्विस वर्कर को बताने का तरीका, बैकग्राउंड स्क्रिप्ट को बताने के तरीके से अलग होता है. इसके अलावा:

  • इनके पास DOM या window इंटरफ़ेस का ऐक्सेस नहीं होता. इसलिए, आपको ऐसे कॉल को किसी दूसरे एपीआई या ऑफ़स्क्रीन दस्तावेज़ में ले जाना होगा.
  • इवेंट लिसनर को, पूरे किए गए वादों के जवाब में या इवेंट कॉलबैक के अंदर रजिस्टर नहीं किया जाना चाहिए.
  • ये XMLHttpRequest() के साथ काम नहीं करते हैं. इसलिए, आपको इस इंटरफ़ेस पर किए गए कॉल को fetch() पर किए गए कॉल से बदलना होगा.
  • इनका इस्तेमाल न होने पर ये बंद हो जाते हैं. इसलिए, आपको ग्लोबल वैरिएबल पर भरोसा करने के बजाय, ऐप्लिकेशन की स्थितियों को बनाए रखना होगा. सर्विस वर्कर को बंद करने से, टाइमर भी पूरे होने से पहले बंद हो सकते हैं. आपको उन्हें अलार्म से बदलना होगा.

इस पेज पर इन टास्क के बारे में ज़्यादा जानकारी दी गई है.

मेनिफ़ेस्ट में "background" फ़ील्ड को अपडेट करें

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

  • manifest.json में "background.scripts" को "background.service_worker" से बदलें. ध्यान दें कि "service_worker" फ़ील्ड में स्ट्रिंग का इस्तेमाल किया जाता है, न कि स्ट्रिंग के ऐरे का.
  • manifest.json से "background.persistent" को हटाएं.
Manifest V2
{
  ...
  "background": {
    "scripts": [
      "backgroundContextMenus.js",
      "backgroundOauth.js"
    ],
    "persistent": false
  },
  ...
}
Manifest V3
{
  ...
  "background": {
    "service_worker": "service_worker.js",
    "type": "module"
  }
  ...
}

"service_worker" फ़ील्ड में सिर्फ़ एक स्ट्रिंग होती है. अगर import कीवर्ड का इस्तेमाल करके ES मॉड्यूल का इस्तेमाल किया जाता है, तो आपको सिर्फ़ "type" फ़ील्ड की ज़रूरत होगी. इसकी वैल्यू हमेशा "module" होगी. ज़्यादा जानकारी के लिए, एक्सटेंशन के सर्विस वर्कर की बुनियादी बातें लेख पढ़ें

DOM और विंडो कॉल को ऑफ़स्क्रीन दस्तावेज़ पर ले जाना

कुछ एक्सटेंशन को, नई विंडो या टैब को विज़ुअल तौर पर खोले बिना, DOM और विंडो ऑब्जेक्ट को ऐक्सेस करने की ज़रूरत होती है. Offscreen API इन इस्तेमाल के उदाहरणों के साथ काम करता है. यह एक्सटेंशन के साथ पैकेज किए गए ऐसे दस्तावेज़ों को खोलता और बंद करता है जो नहीं दिखते. इससे उपयोगकर्ता अनुभव में कोई रुकावट नहीं आती. मैसेज पास करने के अलावा, ऑफ़स्क्रीन दस्तावेज़ अन्य एक्सटेंशन कॉन्टेक्स्ट के साथ एपीआई शेयर नहीं करते हैं. हालांकि, ये एक्सटेंशन के लिए पूरे वेब पेज के तौर पर काम करते हैं, ताकि एक्सटेंशन इनके साथ इंटरैक्ट कर सकें.

Offscreen API का इस्तेमाल करने के लिए, सर्विस वर्कर से एक ऑफ़स्क्रीन दस्तावेज़ बनाएं.

browser.offscreen.createDocument({
  url: browser.runtime.getURL('offscreen.html'),
  reasons: ['CLIPBOARD'],
  justification: 'testing the offscreen API',
});

ऑफ़स्क्रीन दस्तावेज़ में, वह कार्रवाई करें जिसे पहले बैकग्राउंड स्क्रिप्ट में चलाया जाता था. उदाहरण के लिए, होस्ट पेज पर चुने गए टेक्स्ट को कॉपी किया जा सकता है.

let textEl = document.querySelector('#text');
textEl.value = data;
textEl.select();
document.execCommand('copy');

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

localStorage को किसी दूसरे टाइप में बदलना

वेब प्लैटफ़ॉर्म के Storage इंटरफ़ेस (window.localStorage से ऐक्सेस किया जा सकता है) का इस्तेमाल सर्विस वर्कर में नहीं किया जा सकता. इस समस्या को ठीक करने के लिए, इनमें से कोई एक तरीका अपनाएं. सबसे पहले, इसे किसी अन्य स्टोरेज मैकेनिज़्म के कॉल से बदला जा सकता है. ज़्यादातर इस्तेमाल के मामलों के लिए, browser.storage.local नेमस्पेस का इस्तेमाल किया जाएगा. हालांकि, अन्य विकल्प भी उपलब्ध हैं.

इसके कॉल को स्क्रीन पर न दिखने वाले दस्तावेज़ में भी ले जाया जा सकता है. उदाहरण के लिए, localStorage में पहले से सेव किए गए डेटा को किसी दूसरे सिस्टम पर माइग्रेट करने के लिए:

  1. कन्वर्ज़न रूटीन और runtime.onMessage हैंडलर की मदद से, ऑफ़स्क्रीन दस्तावेज़ बनाएं.
  2. ऑफ़स्क्रीन दस्तावेज़ में कन्वर्ज़न रूटीन जोड़ें.
  3. अपने डेटा के लिए, एक्सटेंशन सर्विस वर्कर की जांच browser.storage करें.
  4. अगर आपका डेटा नहीं मिलता है, तो ऑफ़स्क्रीन दस्तावेज़ बनाएं और कन्वर्ज़न रूटीन शुरू करने के लिए runtime.sendMessage() को कॉल करें.
  5. आपने ऑफ़स्क्रीन दस्तावेज़ में जो runtime.onMessage हैंडलर जोड़ा है उसमें कन्वर्ज़न रूटीन को कॉल करें.

एक्सटेंशन में वेब स्टोरेज एपीआई के काम करने के तरीके में भी कुछ बारीकियां होती हैं. ज़्यादा जानने के लिए, स्टोरेज और कुकी पर जाएं.

लिसनर को सिंक्रोनस तरीके से रजिस्टर करें

Manifest V3 में, किसी लिसनर को एसिंक्रोनस तरीके से रजिस्टर करने (उदाहरण के लिए, प्रॉमिस या कॉलबैक के अंदर) की सुविधा काम नहीं करती. यहां दिया गया कोड देखें.

browser.storage.local.get(["badgeText"], ({ badgeText }) => {
  browser.browserAction.setBadgeText({ text: badgeText });
  browser.browserAction.onClicked.addListener(handleActionClick);
});

यह सुविधा, बैकग्राउंड में लगातार चलने वाले पेज के साथ काम करती है. ऐसा इसलिए, क्योंकि पेज लगातार चलता रहता है और कभी भी फिर से शुरू नहीं होता. Manifest V3 में, इवेंट के डिसपैच होने पर सर्विस वर्कर को फिर से शुरू किया जाएगा. इसका मतलब है कि जब इवेंट ट्रिगर होगा, तब लिसनर रजिस्टर नहीं होंगे. ऐसा इसलिए होगा, क्योंकि उन्हें एसिंक्रोनस तरीके से जोड़ा जाता है. इसलिए, इवेंट छूट जाएगा.

इसके बजाय, इवेंट लिसनर रजिस्ट्रेशन को अपनी स्क्रिप्ट के टॉप लेवल पर ले जाएं. इससे यह पक्का होता है कि Chrome, आपके एक्सटेंशन के स्टार्टअप लॉजिक के पूरा होने से पहले ही, आपके ऐक्शन के क्लिक हैंडलर को तुरंत ढूंढकर चालू कर देगा.

browser.action.onClicked.addListener(handleActionClick);

browser.storage.local.get(["badgeText"], ({ badgeText }) => {
  browser.action.setBadgeText({ text: badgeText });
});

XMLHttpRequest() को ग्लोबल फ़ेच() से बदलें

XMLHttpRequest() को किसी सर्विस वर्कर, एक्सटेंशन या अन्य तरीके से कॉल नहीं किया जा सकता. अपनी बैकग्राउंड स्क्रिप्ट में, XMLHttpRequest() को कॉल करने के बजाय ग्लोबल fetch() को कॉल करें.

XMLHttpRequest()
const xhr = new XMLHttpRequest();
console.log('UNSENT', xhr.readyState);

xhr.open('GET', '/api', true);
console.log('OPENED', xhr.readyState);

xhr.onload = () => {
    console.log('DONE', xhr.readyState);
};
xhr.send(null);
fetch()
const response = await fetch('https://www.example.com/greeting.json'')
console.log(response.statusText);

सहमति की स्थितियां सेव करना

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

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

Manifest V2 की बैकग्राउंड स्क्रिप्ट
let savedName = undefined;

browser.runtime.onMessage.addListener(({ type, name }) => {
  if (type === "set-name") {
    savedName = name;
  }
});

browser.browserAction.onClicked.addListener((tab) => {
  browser.tabs.sendMessage(tab.id, { name: savedName });
});

Manifest V3 के लिए, ग्लोबल वैरिएबल को Storage API के कॉल से बदलें.

Manifest V3 सर्विस वर्कर
browser.runtime.onMessage.addListener(({ type, name }) => {
  if (type === "set-name") {
    browser.storage.local.set({ name });
  }
});

browser.action.onClicked.addListener(async (tab) => {
  const { name } = await browser.storage.local.get(["name"]);
  browser.tabs.sendMessage(tab.id, { name });
});

टाइमर को अलार्म में बदलना

setTimeout() या setInterval() तरीकों का इस्तेमाल करके, देरी से होने वाली या समय-समय पर होने वाली कार्रवाइयों को आम तौर पर इस्तेमाल किया जाता है. हालांकि, सर्विस वर्कर के बंद होने पर टाइमर रद्द हो जाते हैं. इसलिए, सर्विस वर्कर में इन एपीआई का इस्तेमाल करने पर गड़बड़ी हो सकती है.

Manifest V2 की बैकग्राउंड स्क्रिप्ट
// 3 minutes in milliseconds
const TIMEOUT = 3 * 60 * 1000;
setTimeout(() => {
  browser.action.setIcon({
    path: getRandomIconPath(),
  });
}, TIMEOUT);

इसके बजाय, Alarms API का इस्तेमाल करें. अन्य लिसनर की तरह, अलार्म लिसनर को भी आपकी स्क्रिप्ट के टॉप लेवल में रजिस्टर किया जाना चाहिए.

Manifest V3 सर्विस वर्कर
async function startAlarm(name, duration) {
  await browser.alarms.create(name, { delayInMinutes: 3 });
}

browser.alarms.onAlarm.addListener(() => {
  browser.action.setIcon({
    path: getRandomIconPath(),
  });
});

सर्विस वर्कर को चालू रखना है

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

किसी ज़्यादा समय तक चलने वाली कार्रवाई के पूरा होने तक, सर्विस वर्कर को चालू रखना

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

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

इन मामलों में, सर्विस वर्कर के मान्य होने की अवधि बढ़ाने के लिए, समय-समय पर एक्सटेंशन एपीआई को कॉल किया जा सकता है. इससे टाइमआउट काउंटर रीसेट हो जाता है. कृपया ध्यान दें कि यह सुविधा सिर्फ़ खास मामलों के लिए है. ज़्यादातर मामलों में, एक जैसा नतीजा पाने के लिए आम तौर पर बेहतर तरीका होता है.

यहां दिए गए उदाहरण में, waitUntil() हेल्पर फ़ंक्शन दिखाया गया है. यह फ़ंक्शन, आपकी सेवा देने वाले वर्कर को तब तक चालू रखता है, जब तक दी गई प्रॉमिस रिज़ॉल्व नहीं हो जाती:

async function waitUntil(promise) {
  const keepAlive = setInterval(browser.runtime.getPlatformInfo, 25 * 1000);
  try {
    await promise;
  } finally {
    clearInterval(keepAlive);
  }
}

waitUntil(someExpensiveCalculation());

किसी सर्विस वर्कर को लगातार चालू रखना

कुछ मामलों में, लाइफ़टाइम को अनिश्चित काल के लिए बढ़ाना ज़रूरी होता है. हमने एंटरप्राइज़ और शिक्षा के क्षेत्र में, इस्तेमाल के सबसे बड़े उदाहरणों की पहचान की है. हम खास तौर पर इन क्षेत्रों में इसकी अनुमति देते हैं. हालांकि, हम सामान्य तौर पर इसकी अनुमति नहीं देते हैं. इन खास मामलों में, सर्विस वर्कर को चालू रखने के लिए, समय-समय पर किसी सामान्य एक्सटेंशन एपीआई को कॉल किया जा सकता है. यह ध्यान रखना ज़रूरी है कि यह सुझाव सिर्फ़ उन एक्सटेंशन पर लागू होता है जो एंटरप्राइज़ या शिक्षा से जुड़े इस्तेमाल के मामलों के लिए, मैनेज किए जा रहे डिवाइसों पर काम करते हैं. अन्य मामलों में इसकी अनुमति नहीं है. साथ ही, Chrome एक्सटेंशन टीम के पास यह अधिकार है कि वह आने वाले समय में ऐसे एक्सटेंशन के ख़िलाफ़ कार्रवाई करे.

अपने सर्विस वर्कर को चालू रखने के लिए, इस कोड स्निपेट का इस्तेमाल करें:

/**
 * Tracks when a service worker was last alive and extends the service worker
 * lifetime by writing the current time to extension storage every 20 seconds.
 * You should still prepare for unexpected termination - for example, if the
 * extension process crashes or your extension is manually stopped at
 * chrome://serviceworker-internals. 
 */
let heartbeatInterval;

async function runHeartbeat() {
  await browser.storage.local.set({ 'last-heartbeat': new Date().getTime() });
}

/**
 * Starts the heartbeat interval which keeps the service worker alive. Call
 * this sparingly when you are doing work which requires persistence, and call
 * stopHeartbeat once that work is complete.
 */
async function startHeartbeat() {
  // Run the heartbeat once at service worker startup.
  runHeartbeat().then(() => {
    // Then again every 20 seconds.
    heartbeatInterval = setInterval(runHeartbeat, 20 * 1000);
  });
}

async function stopHeartbeat() {
  clearInterval(heartbeatInterval);
}

/**
 * Returns the last heartbeat stored in extension storage, or undefined if
 * the heartbeat has never run before.
 */
async function getLastHeartbeat() {
  return (await browser.storage.local.get('last-heartbeat'))['last-heartbeat'];
}