استبدال صفحات الخلفية أو صفحات الأحداث بمشغّل خدمات
يستبدل مشغّل الخدمات صفحة الخلفية أو صفحة الأحداث في الإضافة لضمان بقاء رمز الخلفية خارج سلسلة التعليمات الرئيسية. ويتيح ذلك تشغيل الإضافات عند الحاجة فقط، ما يؤدي إلى توفير الموارد.
كانت صفحات الخلفية مكوّنًا أساسيًا للإضافات منذ طرحها. ببساطة، توفّر صفحات الخلفية بيئة مستقلة عن أي نافذة أو علامة تبويب أخرى. ويسمح ذلك للإضافات بمراقبة الأحداث والتفاعل معها.
تصف هذه الصفحة مهام تحويل صفحات الخلفية إلى مشغّلي خدمات الإضافات. لمزيد من المعلومات حول مشغّلي خدمات الإضافات بشكل عام، يُرجى الاطّلاع على الدرس التعليمي معالجة الأحداث باستخدام مشغّلي الخدمات وقسم لمحة عن مشغّلي خدمات الإضافات.
الاختلافات بين النصوص البرمجية للخلفية ومشغّلي خدمات الإضافات
في بعض السياقات، ستظهر لك عبارة "النصوص البرمجية للخلفية" للإشارة إلى مشغّلي خدمات الإضافات. على الرغم من أنّ مشغّلي خدمات الإضافات يتم تشغيلهم في الخلفية، فإنّ تسميتهم بالنصوص البرمجية للخلفية مضلّلة إلى حد ما لأنّها توحي بقدرات متطابقة. في ما يلي وصف للاختلافات بينهما.
التغييرات التي تم إجراؤها على صفحات الخلفية
يختلف مشغّلو الخدمات عن صفحات الخلفية في عدد من الجوانب.
- يتم تشغيلهم خارج سلسلة التعليمات الرئيسية، ما يعني أنّهم لا يؤثرون في محتوى الإضافة.
- لديهم قدرات خاصة، مثل اعتراض أحداث الإحضار على مصدر الإضافة، مثل الأحداث الواردة من نافذة منبثقة في شريط الأدوات.
- يمكنهم التواصل والتفاعل مع السياقات الأخرى من خلال واجهة Clients.
التغييرات التي عليك إجراؤها
عليك إجراء بعض التعديلات على الرمز البرمجي لمراعاة الاختلافات بين طريقة عمل النصوص البرمجية للخلفية ومشغّلي الخدمات. في البداية، تختلف طريقة تحديد مشغّل الخدمات في ملف البيان عن طريقة تحديد النصوص البرمجية للخلفية. علاوة على ذلك:
- بما أنّه لا يمكنهم الوصول إلى نموذج العناصر في المستند (DOM) أو واجهة
window، عليك نقل عمليات الاستدعاء هذه إلى واجهة برمجة تطبيقات مختلفة أو إلى مستند خارج الشاشة. - يجب عدم تسجيل أدوات معالجة الأحداث استجابةً للوعود التي تم إرجاعها أو داخل عمليات معاودة الاتصال بالأحداث.
- بما أنّهم لا يتوافقون مع
XMLHttpRequest()، عليك استبدال عمليات استدعاء هذه الواجهة بعمليات استدعاءfetch(). - بما أنّهم يتوقفون عن العمل عندما لا يكونون قيد الاستخدام، عليك الاحتفاظ بحالات التطبيق بدلاً من الاعتماد على المتغيّرات العامة. يمكن أن يؤدي إيقاف مشغّلي الخدمات أيضًا إلى إنهاء المؤقّتات قبل اكتمالها. عليك استبدالها بالمنبّهات.
تصف هذه الصفحة هذه المهام بالتفصيل.
تعديل حقل "الخلفية" في ملف البيان
في الإصدار 3 من ملف البيان، يتم استبدال صفحات الخلفية بمشغّل خدمات. في ما يلي التغييرات التي تم إجراؤها على ملف البيان.
- استبدِل
"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 (باستخدام الك3}import الكلمة الرئيسية). ستكون قيمته دائمًا "module". لمزيد من المعلومات، يُرجى الاطّلاع على أساسيات مشغّل خدمات الإضافات
نقل عمليات استدعاء نموذج العناصر في المستند (DOM) ونافذة إلى مستند خارج الشاشة
تحتاج بعض الإضافات إلى الوصول إلى نموذج العناصر في المستند (DOM) وعناصر النافذة بدون فتح نافذة أو علامة تبويب جديدة بشكل مرئي. تتيح واجهة برمجة التطبيقات Offscreen حالات الاستخدام هذه من خلال فتح المستندات غير المعروضة والمرفقة بالإضافة وإغلاقها، بدون التأثير سلبًا في تجربة المستخدم. باستثناء تمرير الرسائل، لا تشارك المستندات خارج الشاشة واجهات برمجة التطبيقات مع سياقات الإضافات الأخرى، ولكنّها تعمل كصفحات ويب كاملة لتتفاعل معها الإضافات.
لاستخدام واجهة برمجة التطبيقات Offscreen، أنشِئ مستندًا خارج الشاشة من مشغّل الخدمات.
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.storageبحثًا عن بياناتك. - إذا لم يتم العثور على بياناتك، أنشِئ مستندًا خارج الشاشة واستدعِ
runtime.sendMessage()لبدء روتين التحويل. - في المعالج
runtime.onMessageالذي أضفته إلى المستند خارج الشاشة، استدعِ روتين التحويل.
هناك أيضًا بعض الفروقات الدقيقة في طريقة عمل واجهات برمجة تطبيقات التخزين على الويب في الإضافات. مزيد من المعلومات في مساحة التخزين وملفات تعريف الارتباط.
تسجيل أدوات المعالجة بشكل متزامن
لا يُضمَن أن يعمل تسجيل متتبِّع بشكل غير متزامن (على سبيل المثال، داخل عمليّة غير مكتملة أو معاودة اتصال) في الإصدار 3 من ملف البيان. ضَع في اعتبارك الرمز البرمجي التالي.
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 من ملف البيان، استبدِل المتغيّر العام بعملية استدعاء لواجهة برمجة تطبيقات التخزين.
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. كما هو الحال مع أدوات المعالجة الأخرى، يجب تسجيل أدوات معالجة المنبّهات في المستوى الأعلى من النص البرمجي.
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 بالحق في اتخاذ إجراءات ضد هذه الإضافات في المستقبل.
استخدِم مقتطف الرمز البرمجي التالي لإبقاء مشغّل الخدمات نشطًا:
/**
* 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'];
}