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

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

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

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

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

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

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

बैकग्राउंड पेजों से बदलाव

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

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

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

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

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

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

मेनिफ़ेस्ट में "बैकग्राउंड" फ़ील्ड को अपडेट करना

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" फ़ील्ड में सिर्फ़ एक स्ट्रिंग डाली जा सकती है. आपको "type" फ़ील्ड की ज़रूरत सिर्फ़ तब होगी, जब ईएस मॉड्यूल का इस्तेमाल किया जा रहा हो. इसके लिए, import कीवर्ड का इस्तेमाल किया जाता है. इसकी वैल्यू हमेशा "module" होगी. ज़्यादा जानकारी के लिए, एक्सटेंशन सर्विस वर्कर से जुड़ी बुनियादी जानकारी देखें

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() की जगह ग्लोबल fetch() का इस्तेमाल करना

XMLHttpRequest() को सर्विस वर्कर, एक्सटेंशन या किसी अन्य तरीके से कॉल नहीं किया जा सकता. अपनी बैकग्राउंड स्क्रिप्ट से XMLHttpRequest() को किए जाने वाले कॉल को global 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);

Persist states

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

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

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'];
}