استبدال الخلفية أو صفحات الأحداث بمشغّل خدمات
يحلّ مشغّل الخدمات محلّ صفحة الخلفية أو صفحة الأحداث الخاصة بالإضافة لضمان بقاء رمز الخلفية خارج سلسلة التعليمات الرئيسية. يتيح ذلك تشغيل الإضافات عند الحاجة إليها فقط، ما يؤدي إلى توفير الموارد.
شكّلت صفحات الخلفية عنصرًا أساسيًا في الإضافات منذ طرحها. ببساطة، توفّر صفحات الخلفية بيئة مستقلة عن أي نافذة أو علامة تبويب أخرى. يتيح ذلك للإضافات مراقبة الأحداث واتّخاذ إجراءات استجابةً لها.
توضّح هذه الصفحة المهام اللازمة لتحويل صفحات الخلفية إلى عاملي خدمة الإضافات. لمزيد من المعلومات حول مشغِّلات الخدمات في الإضافات بشكل عام، يُرجى الاطّلاع على البرنامج التعليمي التعامل مع الأحداث باستخدام مشغِّلات الخدمات والقسم لمحة عن مشغِّلات الخدمات في الإضافات.
الاختلافات بين البرامج النصية التي تعمل في الخلفية وخدمات العاملين في الإضافات
في بعض السياقات، ستظهر لك أدوات خدمة الإضافات التي تُعرف باسم "نصوص برمجية في الخلفية". على الرغم من أنّ مشغّلي الخدمات في الإضافات يعملون في الخلفية، إلا أنّ تسميتهم بالنصوص البرمجية التي تعمل في الخلفية مضلّلة إلى حدّ ما لأنّها تشير إلى إمكانات متطابقة. في ما يلي وصف للاختلافات.
التغييرات من الصفحات التي تعمل في الخلفية
تختلف أدوات Service Worker عن صفحات الخلفية في عدد من الجوانب.
- تعمل هذه الإضافات خارج سلسلة التعليمات البرمجية الرئيسية، ما يعني أنّها لا تتداخل مع محتوى الإضافة.
- تتضمّن هذه البرامج إمكانات خاصة، مثل اعتراض أحداث الجلب من مصدر الإضافة، مثل تلك التي تظهر من نافذة منبثقة في شريط الأدوات.
- يمكنهم التواصل والتفاعل مع سياقات أخرى من خلال واجهة العملاء.
التغييرات التي عليك إجراؤها
عليك إجراء بعض التعديلات على الرمز البرمجي لمراعاة الاختلافات بين طريقة عمل النصوص البرمجية التي تعمل في الخلفية وخدمات العاملين. في البداية، تختلف طريقة تحديد مشغّل الخدمات في ملف البيان عن طريقة تحديد النصوص البرمجية التي تعمل في الخلفية. علاوة على ذلك:
- بما أنّه لا يمكنهم الوصول إلى نموذج المستند أو واجهة
window، عليك نقل هذه الطلبات إلى واجهة برمجة تطبيقات مختلفة أو إلى مستند خارج الشاشة. - يجب عدم تسجيل أدوات معالجة الأحداث استجابةً للوعود التي تم إرجاعها أو داخل عمليات رد الاتصال الخاصة بالأحداث.
- بما أنّها غير متوافقة مع الإصدارات القديمة من
XMLHttpRequest()، عليك استبدال عمليات استدعاء هذه الواجهة بعمليات استدعاءfetch(). - وبما أنّها تتوقف عند عدم الاستخدام، عليك الاحتفاظ بحالات التطبيق بدلاً من الاعتماد على المتغيرات العامة. يمكن أن يؤدي إنهاء عمل برامج الخدمة إلى إنهاء المؤقتات قبل اكتمالها. عليك استبدالها بالمنبّهات.
توضّح هذه الصفحة هذه المهام بالتفصيل.
تعديل الحقل "الخلفية" في البيان
في الإصدار Manifest V3، يتم استبدال صفحات الخلفية بمشغّل خدمات. في ما يلي قائمة بالتغييرات في ملف البيان.
- استبدِل
"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، أنشئ مستندًا خارج الشاشة من عامل الخدمة.
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 إلى آلية أخرى، اتّبِع الخطوات التالية:
- أنشئ مستندًا خارج الشاشة يتضمّن روتين تحويل ومعالج
runtime.onMessage. - أضِف روتين إحالة ناجحة إلى المستند خارج الشاشة.
- في مشغّل خدمات الإضافة، ابحث عن
browser.storageللوصول إلى بياناتك. - إذا لم يتم العثور على بياناتك، أنشئ مستندًا خارج الشاشة واستدعِ
runtime.sendMessage()لبدء إجراء التحويل. - في معالج
runtime.onMessageالذي أضفته إلى المستند خارج الشاشة، استدعِ روتين التحويل.
هناك أيضًا بعض الفروق الدقيقة في طريقة عمل واجهات برمجة التطبيقات لتخزين البيانات على الويب في الإضافات. يمكنك الاطّلاع على مزيد من المعلومات في مساحة التخزين وملفات تعريف الارتباط.
تسجيل أدوات المعالجة بشكل متزامن
لا يُضمَن عمل متتبِّع بشكل غير متزامن (على سبيل المثال، داخل عمليّة غير مكتملة أو ردّ اتصال) في Manifest V3. ضَع الرمز التالي في الاعتبار.
browser.storage.local.get(["badgeText"], ({ badgeText }) => {
browser.browserAction.setBadgeText({ text: badgeText });
browser.browserAction.onClicked.addListener(handleActionClick);
});
يعمل هذا الإجراء مع صفحة خلفية ثابتة لأنّ الصفحة تعمل باستمرار ولا تتم إعادة تهيئتها أبدًا. في الإصدار 3 من ملف البيان، ستتم إعادة تهيئة مشغّل الخدمات عند إرسال الحدث. وهذا يعني أنّه عند تشغيل الحدث، لن يتم تسجيل أدوات معالجة الحدث (لأنّها تتم إضافتها بشكل غير متزامن)، وبالتالي لن يتم رصد الحدث.
بدلاً من ذلك، يمكنك نقل تسجيل متتبِّع الأحداث إلى المستوى الأعلى من النص البرمجي. يضمن ذلك أن يتمكّن Chrome من العثور على معالج النقر الخاص بالإجراء واستدعائه على الفور، حتى إذا لم تنتهِ الإضافة من تنفيذ منطق بدء التشغيل.
browser.action.onClicked.addListener(handleActionClick);
browser.storage.local.get(["badgeText"], ({ badgeText }) => {
browser.action.setBadgeText({ text: badgeText });
});
استبدال XMLHttpRequest() بـ fetch() العامة
لا يمكن الاتصال بـ XMLHttpRequest() من مشغّل خدمات أو إضافة أو غير ذلك. استبدِل طلبات البحث التي يتم إجراؤها من نص الخلفية البرمجية XMLHttpRequest() بطلبات بحث يتم إجراؤها إلى global 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; 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.
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(). ومع ذلك، يمكن أن تتعذّر هذه واجهات برمجة التطبيقات في عاملي الخدمة لأنّه يتم إلغاء المؤقتات كلما تم إنهاء عامل الخدمة.
// 3 minutes in milliseconds const TIMEOUT = 3 * 60 * 1000; setTimeout(() => { browser.action.setIcon({ path: getRandomIconPath(), }); }, TIMEOUT);
يمكنك بدلاً من ذلك استخدام Alarms API. كما هو الحال مع أدوات معالجة الأحداث الأخرى، يجب تسجيل أدوات معالجة الأحداث الخاصة بالمنبّهات في المستوى الأعلى من النص البرمجي.
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'];
}