استبدال صفحات الخلفية أو الأحداث بمشغّل خدمات
يستبدل مشغّل الخدمة صفحة الخلفية أو صفحة الحدث في الإضافة لضمان بقاء رمز الخلفية خارج سلسلة المهام الرئيسية. ويتيح ذلك تشغيل الإضافات عند الحاجة فقط، ما يؤدي إلى توفير الموارد.
كانت صفحات الخلفية عنصرًا أساسيًا في الإضافات منذ طرحها. بعبارة أخرى، توفّر الصفحات التي تعمل في الخلفية بيئة مستقلة عن أي نافذة أو علامة تبويب أخرى. يتيح ذلك للإضافات مراقبة الأحداث واتّخاذ الإجراءات اللازمة استجابةً لها.
توضّح هذه الصفحة مهام تحويل الصفحات التي تعمل في الخلفية إلى عناصر خدمة إضافية. لمزيد من المعلومات عن مشغِّلات خدمات الإضافات بشكل عام، اطّلِع على الدليل التعليمي معالجة الأحداث باستخدام مشغِّلات الخدمات والقسم لمحة عن مشغِّلات خدمات الإضافات.
الاختلافات بين النصوص البرمجية التي تعمل في الخلفية وخدمات إضافة المهام
في بعض السياقات، سيظهر لك مشغّلو خدمات الإضافات الذين يُطلق عليهم "النصوص البرمجية التي تعمل في الخلفية". على الرغم من أنّ مشغّلي خدمات الإضافات يتم تشغيلهم في الخلفية، إلا أنّ تسميتهم بنصوص برمجية تعمل في الخلفية قد تؤدي إلى التباس إلى حدّ ما من خلال الإيحاء بقدرات متطابقة. ويمكنك الاطّلاع على الاختلافات أدناه.
التغييرات من الصفحات التي تعمل في الخلفية
هناك عدد من الاختلافات بين مهام الخدمة والصفحات التي تعمل في الخلفية.
- تعمل هذه الطلبات خارج سلسلة المحادثات الرئيسية، ما يعني أنّها لا تتداخل مع محتوى الإضافة.
- وتتميّز هذه الواجهة بإمكانيات خاصة مثل اعتراض أحداث الجلب من مصدر الإضافة، مثل تلك الواردة من نافذة منبثقة في شريط الأدوات.
- ويمكنهم التواصل والتفاعل مع السياقات الأخرى من خلال واجهة العملاء.
التغييرات التي يجب إجراؤها
ستحتاج إلى إجراء بعض التعديلات على الرمز البرمجي لمراعاة الاختلافات بين طريقة عمل النصوص البرمجية التي تعمل في الخلفية وطريقة عمل مهام الخدمة. في البداية، تختلف الطريقة التي يتم بها تحديد مشغّل الخدمات في ملف البيان عن طريقة تحديد النصوص البرمجية للخلفية. علاوةً على ذلك:
- وبما أنّه لا يمكنها الوصول إلى DOM أو واجهة
window
، عليك نقل هذه الطلبات إلى واجهة برمجة تطبيقات مختلفة أو إلى مستند خارج الشاشة. - يجب عدم تسجيل أدوات معالجة الأحداث استجابةً للوعود التي تم إرجاعها أو داخل عمليات استدعاء الأحداث.
- وبما أنّها غير متوافقة مع الإصدارات القديمة من
XMLHttpRequest()
، عليك استبدال طلبات البيانات من هذه الواجهة بطلبات البيانات منfetch()
. - وبما أنّها تنتهي عند عدم استخدامها، عليك الاحتفاظ بحالات التطبيق بدلاً من الاعتماد على المتغيّرات الشاملة. يمكن أن يؤدي إنهاء مهام الخدمة أيضًا إلى إنهاء الموقّتات قبل اكتمالها. يجب استبدالها بالمنبهات.
توضّح هذه الصفحة هذه المهام بالتفصيل.
تعديل الحقل background في البيان
في الإصدار 3 من Manifest، يتم استبدال الصفحات التي تعمل في الخلفية بمشغّل خدمات. في ما يلي التغييرات في البيان.
- استبدِل
"background.scripts"
بـ"background.service_worker"
فيmanifest.json
. يُرجى العلم أنّ حقل"service_worker"
يقبل سلسلة، وليس صفيفًا من السلاسل. - أزِل
"background.persistent"
منmanifest.json
.
{ ... "background": { "scripts": [ "backgroundContextMenus.js", "backgroundOauth.js" ], "persistent": false }, ... }
{ ... "background": { "service_worker": "service_worker.js", "type": "module" } ... }
يقبل الحقل "service_worker"
سلسلة واحدة. لن تحتاج إلى حقل "type"
إلا إذا كنت تستخدِم وحدات ES (باستخدام الكلمة الرئيسية import
). ستكون قيمته دائمًا "module"
. لمزيد من المعلومات، يُرجى الاطّلاع على أساسيات مهام الخدمة في الإضافات.
نقل طلبات DOM والنوافذ إلى مستند خارج الشاشة
تحتاج بعض الإضافات إلى الوصول إلى عناصر DOM وعناصر النافذة بدون فتح نافذة أو علامة تبويب جديدة بشكل مرئي. تتوافق واجهة برمجة التطبيقات Offscreen API مع حالات الاستخدام هذه من خلال فتح وإغلاق المستندات غير المعروضة المُضمّنة في الإضافة، بدون تعطيل تجربة المستخدم. باستثناء نقل الرسائل، لا تشارك المستندات التي لا تظهر على الشاشة واجهات برمجة التطبيقات مع سياقات الإضافات الأخرى، ولكنها تعمل كصفحات ويب كاملة يمكن للإضافة التفاعل معها.
لاستخدام Offscreen API، أنشئ مستندًا خارج الشاشة من الخدمة العاملة.
chrome.offscreen.createDocument({
url: chrome.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
) في مشغّل الخدمات. لحلّ هذه المشكلة، يمكنك اتّخاذ أحد الإجراءَين التاليَين: أولاً، يمكنك استبدالها بالطلبات لآلية تخزين أخرى. ستُستخدم مساحة الاسم chrome.storage.local
معظم حالات الاستخدام، ولكن تتوفّر خيارات أخرى.
يمكنك أيضًا نقل المكالمات إلى مستند خارج الشاشة. على سبيل المثال، لنقل البيانات المخزّنة سابقًا في localStorage
إلى آلية أخرى:
- أنشئ مستندًا خارج الشاشة باستخدام سلسلة إجراءات تحويل ومعالج
runtime.onMessage
. - أضِف سلسلة إجراءات إحالات ناجحة إلى المستند خارج الشاشة.
- في عامل خدمة إضافة Chrome، تحقّق من
chrome.storage
لبياناتك. - إذا لم يتم العثور على بياناتك، يمكنك create مستند غير معروض على الشاشة والاتصال برقم
runtime.sendMessage()
لبدء عملية التحويل. - في معالِج "
runtime.onMessage
" الذي أضفته إلى المستند خارج الشاشة، يمكنك طلب سلسلة إجراءات الإحالات الناجحة.
هناك أيضًا بعض الاختلافات الدقيقة في آلية عمل واجهات برمجة التطبيقات لتخزين الويب في الإضافات. اطّلِع على مزيد من المعلومات في مقالة مساحة التخزين وملفات تعريف الارتباط.
تسجيل أدوات المعالجة بشكل متزامن
لا يمكن ضمان عمل تسجيل مستمع بشكل غير متزامن (على سبيل المثال، داخل وعد أو دالة استدعاء) في Manifest V3. راجِع الرمز البرمجي التالي.
chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
chrome.browserAction.setBadgeText({ text: badgeText });
chrome.browserAction.onClicked.addListener(handleActionClick);
});
يعمل هذا الإجراء مع صفحة دائمة في الخلفية لأنّ الصفحة تعمل باستمرار ولا تتم إعادة بدء تشغيلها أبدًا. في الإصدار 3 من بيان الإضافة، ستتم إعادة ضبط الخدمة العاملة عند إرسال الحدث. وهذا يعني أنّه عند بدء الحدث، لن يتم تسجيل أدوات معالجة الأحداث (لأنّها تتم إضافتها بشكل غير متزامن)، ولن يتم رصد الحدث.
بدلاً من ذلك، انقل تسجيل مستمع الحدث إلى المستوى الأعلى من النص البرمجي. يضمن ذلك أنّ Chrome سيتمكّن من العثور على معالِج النقر الخاص بالإجراء وتنفيذه على الفور، حتى إذا لم تكن الإضافة قد أكملت تنفيذ منطق بدء التشغيل.
chrome.action.onClicked.addListener(handleActionClick);
chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
chrome.action.setBadgeText({ text: badgeText });
});
استبدل XMLHttpRequest() بـFetch() عمومي
لا يمكن استدعاء XMLHttpRequest()
من عامل خدمة أو إضافة أو غير ذلك. استبدِل المكالمات الواردة من نص الخلفية إلى XMLHttpRequest()
بالمكالمات الواردة إلى fetch()
العام.
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);
const response = await fetch('https://www.example.com/greeting.json'') console.log(response.statusText);
حالات الاحتفاظ
وظائف الخدمة مؤقتة، ما يعني أنّه من المرجّح أن يتم تشغيلها وتنفيذها وإنهائها بشكل متكرّر أثناء جلسة المتصفّح للمستخدم. ويعني ذلك أيضًا أنّ البيانات لا تتوفّر على الفور في المتغيّرات العمومية منذ إلغاء السياق السابق. لحلّ هذه المشكلة، استخدِم واجهات برمجة تطبيقات مساحة التخزين كمصدر موثوق للبيانات. سيوضح مثال كيفية القيام بذلك.
يستخدم المثال التالي متغيّرًا عامًا لتخزين اسم. في مشغّل الخدمات، يمكن إعادة ضبط هذا المتغيّر عدة مرات على مدار جلسة متصفّح المستخدم.
let savedName = undefined; chrome.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { savedName = name; } }); chrome.browserAction.onClicked.addListener((tab) => { chrome.tabs.sendMessage(tab.id, { name: savedName }); });
بالنسبة إلى الإصدار 3 من ملف البيان، استبدِل المتغيّر العام باستدعاء Storage API.
chrome.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { chrome.storage.local.set({ name }); } }); chrome.action.onClicked.addListener(async (tab) => { const { name } = await chrome.storage.local.get(["name"]); chrome.tabs.sendMessage(tab.id, { name }); });
تحويل الموقتات إلى منبّهات
من الشائع استخدام عمليات متأخرة أو دورية باستخدام الطريقتَين setTimeout()
أو setInterval()
. ومع ذلك، يمكن أن تتعذّر استخدام واجهات برمجة التطبيقات هذه في مشغّلات الخدمات لأنّه يتم إلغاء الموقّتات عند إنهاء مشغّل الخدمة.
// 3 minutes in milliseconds const TIMEOUT = 3 * 60 * 1000; setTimeout(() => { chrome.action.setIcon({ path: getRandomIconPath(), }); }, TIMEOUT);
ويمكنك استخدام Alarms API بدلاً من ذلك. كما هو الحال مع المستمعين الآخرين، يجب تسجيل أدوات استماع التنبيه في المستوى الأعلى من النص.
async function startAlarm(name, duration) { await chrome.alarms.create(name, { delayInMinutes: 3 }); } chrome.alarms.onAlarm.addListener(() => { chrome.action.setIcon({ path: getRandomIconPath(), }); });
إبقاء الخدمة نشطة
إنّ مشغّلات الخدمات مستندة بطبيعتها إلى الأحداث، وسيتم إنهاءها في حال عدم النشاط. وبهذه الطريقة، يمكن لمتصفّح Chrome تحسين أداء إضافتك واستهلاكها للذاكرة. يمكنك الاطّلاع على مزيد من المعلومات في مستندات دورة حياة مهام الخدمة. قد تتطلّب الحالات الاستثنائية اتخاذ إجراءات إضافية لضمان استمرار عمل الخدمة لفترة أطول.
إبقاء عامل الخدمة نشطًا إلى أن تكتمل عملية طويلة الأمد
أثناء عمليات مشغّل الخدمات التي تستغرق وقتًا طويلاً ولا تستدعي واجهات برمجة تطبيقات الإضافات، قد يتم إيقاف مشغّل الخدمات أثناء العملية. تشمل الأمثلة ما يلي:
- طلب
fetch()
يستغرق وقتًا أطول من خمس دقائق (مثل تنزيل ملف كبير على اتصال قد يكون ضعيفًا) - يشير ذلك المصطلح إلى عملية حسابية معقّدة غير متزامنة تستغرق أكثر من 30 ثانية.
لإطالة مدة صلاحية الخدمة في هذه الحالات، يمكنك استدعاء واجهة برمجة تطبيقات بسيطة للإضافة بشكل دوري لإعادة ضبط مقياس المهلة. يُرجى العِلم أنّ هذا الإجراء مخصّص للحالات الاستثنائية فقط، وفي معظم الحالات، تتوفّر عادةً طريقة أفضل وأكثر ملاءمةً للمنصة لتحقيق النتيجة نفسها.
يوضّح المثال التالي إحدى وظائف المساعدة "waitUntil()
" التي تحافظ على نشاط مشغّل الخدمات إلى أن يتم حلّ وعد معيّن:
async function waitUntil(promise) = {
const keepAlive = setInterval(chrome.runtime.getPlatformInfo, 25 * 1000);
try {
await promise;
} finally {
clearInterval(keepAlive);
}
}
waitUntil(someExpensiveCalculation());
إبقاء عامل الخدمة نشطًا باستمرار
في حالات نادرة، يكون من الضروري تمديد مدة الصلاحية إلى أجل غير مسمى. لقد حدّدنا المؤسسات التعليمية على أنّها أكبر حالات الاستخدام، ونسمح بذلك تحديدًا في هذه المؤسسات، ولكننا لا نوفّر هذه الميزة بشكل عام. في هذه الظروف الاستثنائية، يمكن الحفاظ على تشغيل عامل الخدمة من خلال استدعاء واجهة برمجة تطبيقات بسيطة للإضافة بشكل دوري. من المهمّ ملاحظة أنّ هذا الاقتراح لا ينطبق إلا على الإضافات التي تعمل على الأجهزة المُدارة لحالات الاستخدام في المؤسسات أو المؤسسات التعليمية. ولا يُسمح بذلك في الحالات الأخرى، ويحتفظ فريق إضافات Chrome بالحق في اتّخاذ إجراء ضد هذه الإضافات في المستقبل.
استخدِم مقتطف الرمز التالي للحفاظ على تشغيل worker الخدمة:
/**
* 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 chrome.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 chrome.storage.local.get('last-heartbeat'))['last-heartbeat'];
}