التعامل مع الفعاليات مع موظفي الخدمات

دليل تعليمي يتناول مفاهيم "عامل الخدمة" لإضافة الخدمة

نظرة عامة

يقدّم هذا البرنامج التعليمي مقدمة للعاملين في خدمة إضافات Chrome. كجزء من هذا الدليل التمهيدي، ستنشئ إضافة تتيح للمستخدمين الانتقال بسرعة إلى صفحات مرجع واجهة برمجة التطبيقات Chrome API باستخدام مربّع البحث الشامل. ستتعرّف على كيفية:

  • سجِّل مشغّل الخدمات وأدخِل الوحدات.
  • تصحيح أخطاء عامل خدمة الإضافة
  • إدارة الحالة والتعامل مع الأحداث
  • تشغيل الأحداث الدورية
  • التواصل مع النصوص البرمجية للمحتوى

قبل البدء

يفترض هذا الدليل أنّ لديك خبرة أساسية في تطوير الويب. ننصحك بمراجعة الدليل الأساسي للإضافة ومرحبًا بك للحصول على مقدمة عن تطوير الإضافات.

إنشاء الإضافة

ابدأ بإنشاء دليل جديد باسم quick-api-reference لتخزين ملفات الإضافات، أو نزِّل رمز المصدر من مستودع عيّنات GitHub.

الخطوة 1: تسجيل الخدمة العاملة

أنشئ ملف manifest في جذر المشروع وأضِف الرمز البرمجي التالي:

manifest.json:

{
  "manifest_version": 3,
  "name": "Open extension API reference",
  "version": "1.0.0",
  "icons": {
    "16": "images/icon-16.png",
    "128": "images/icon-128.png"
  },
  "background": {
    "service_worker": "service-worker.js"
  }
}

تسجِّل الإضافات عامل الخدمة في البيان الذي لا يستخدِم سوى ملف JavaScript واحد. لا حاجة إلى طلب navigator.serviceWorker.register()، كما تفعل في صفحة ويب.

أنشئ مجلد images ثم نزِّل الرموز فيه.

اطّلِع على الخطوات الأولى من الدليل التعليمي "وقت القراءة" للتعرّف على مزيد من المعلومات عن بيانات التعريف والرموز في البيان.

الخطوة 2: استيراد وحدات متعددة لمشغّلي الخدمات

ينفِّذ عامل الخدمة لدينا ميزتَين. لضمان إمكانية صيانة الميزة بشكل أفضل، سننفّذ كل ميزة في وحدة منفصلة. أولاً، علينا تحديد الخدمة العاملة على أنّها وحدة ES في البيان، ما يسمح لنا باستيراد الوحدات في الخدمة العاملة:

manifest.json:

{
 "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  },
}

أنشئ ملف service-worker.js واستورِد وحدتَين:

import './sw-omnibox.js';
import './sw-tips.js';

أنشئ هذه الملفات وأضِف سجلّ وحدة تحكّم إلى كل ملف.

sw-omnibox.js:

console.log("sw-omnibox.js");

sw-tips.js:

console.log("sw-tips.js");

اطّلِع على استيراد النصوص البرمجية للتعرّف على طرق أخرى لاستيراد ملفات متعددة في عامل خدمة.

اختياري: تصحيح أخطاء الخدمة العاملة

سأشرح كيفية العثور على سجلات الخدمة العاملة ومعرفة وقت إنهائها. أولاً، اتّبِع التعليمات لتحميل إضافة غير مُعبَّأة.

بعد 30 ثانية، ستظهر لك رسالة "عامل الخدمة (غير نشط)"، ما يعني أنّ عامل الخدمة قد تم إنهاءه. انقر على الرابط "مشغّل الخدمات (غير نشط)" لفحصه. توضّح الصورة المتحركة التالية ذلك.

هل لاحظت أنّ فحص مشغّل الخدمة قد أيقظه؟ سيؤدي فتح الخدمة العاملة في أدوات المطوّر إلى إبقائها نشطة. للتأكّد من أنّ الإضافة تعمل بشكل صحيح عند إنهاء الخدمة، احرص على إغلاق "أدوات المطوّر".

الآن، يمكنك تقسيم الإضافة لمعرفة مكان تحديد الأخطاء. وتتمثّل إحدى الطرق لإجراء ذلك في حذف ‎ ".js" من عملية استيراد './sw-omnibox.js' في ملف service-worker.js. لن يتمكّن Chrome من تسجيل مشغّل الخدمة.

ارجع إلى chrome://extensions وأعِد تحميل الإضافة. ستظهر لك خطأان:

Service worker registration failed. Status code: 3.

An unknown error occurred when fetching the script.

اطّلِع على تصحيح أخطاء الإضافات للتعرّف على المزيد من الطرق لتصحيح أخطاء عامل الخدمة في الإضافة.

الخطوة 4: بدء الحالة

سيوقف Chrome خدمات "العامل" إذا لم تكن مطلوبة. نستخدم واجهة برمجة التطبيقات chrome.storage للحفاظ على الحالة في جميع جلسات الخدمة العاملة. للوصول إلى مساحة التخزين، علينا طلب إذن في ملف البيان:

manifest.json:

{
  ...
  "permissions": ["storage"],
}

أولاً، احفظ الاقتراحات الافتراضية في التخزين. يمكننا بدء الحالة عند تثبيت الإضافة لأول مرة من خلال الاستماع إلى حدث runtime.onInstalled():

sw-hybrid.js:

...
// Save default API suggestions
chrome.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'install') {
    chrome.storage.local.set({
      apiSuggestions: ['tabs', 'storage', 'scripting']
    });
  }
});

لا يمكن لعمال الخدمة الوصول مباشرةً إلى عنصر النافذة، وبالتالي لا يمكنهم استخدام window.localStorage لتخزين القيم. بالإضافة إلى ذلك، وحدات الخدمة هي بيئات تنفيذ قصيرة الأجل، ويُنهى تشغيلها بشكل متكرّر خلال جلسة المتصفّح للمستخدم، ما يجعلها غير متوافقة مع المتغيّرات الكلية. بدلاً من ذلك، يمكنك استخدام أداة chrome.storage.local التي تخزِّن البيانات على الجهاز المحلي.

اطّلِع على تخزين البيانات بدلاً من استخدام المتغيّرات الشاملة للتعرّف على خيارات التخزين الأخرى لعمال خدمات الإضافات.

الخطوة 5: تسجيل الأحداث

يجب تسجيل جميع مستمعي الأحداث بشكل ثابت في النطاق العام لخدمة Worker. بعبارة أخرى، يجب عدم تداخل معالجات الأحداث في الدوال غير المتزامنة. وبهذه الطريقة، يضمن Chrome استعادة جميع معالِجات الأحداث في حال إعادة تشغيل عامل الخدمة.

في هذا المثال، سنستخدم واجهة برمجة التطبيقات chrome.omnibox، ولكن علينا أولاً الإفصاح عن عامل تشغيل الكلمات الرئيسية في مربّع البحث الشامل في البيان:

manifest.json:

{
  ...
  "minimum_chrome_version": "102",
  "omnibox": {
    "keyword": "api"
  },
}

يمكنك الآن تسجيل أدوات معالجة أحداث المربّع المتعدد الاستخدامات في المستوى الأعلى من النص البرمجي. عندما يُدخِل المستخدم الكلمة الرئيسية للمربّع المتعدّد الاستخدامات (api) في شريط العناوين متبوعة بعلامة تبويب أو مسافة، سيعرض Chrome قائمة بالاقتراحات استنادًا إلى الكلمات الرئيسية في مساحة التخزين. إنّ الحدث onInputChanged()، الذي يأخذ إدخال المستخدم الحالي وعنصر suggestResult، هو المسؤول عن تعبئة هذه الاقتراحات.

sw-hybrid.js:

...
const URL_CHROME_EXTENSIONS_DOC =
  'https://developer.chrome.com/docs/extensions/reference/';
const NUMBER_OF_PREVIOUS_SEARCHES = 4;

// Display the suggestions after user starts typing
chrome.omnibox.onInputChanged.addListener(async (input, suggest) => {
  await chrome.omnibox.setDefaultSuggestion({
    description: 'Enter a Chrome API or choose from past searches'
  });
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  const suggestions = apiSuggestions.map((api) => {
    return { content: api, description: `Open chrome.${api} API` };
  });
  suggest(suggestions);
});

بعد أن يختار المستخدم اقتراحًا، سيفتح onInputEntered() الصفحة المرجعية المقابلة لواجهة Chrome API.

sw-hybrid.js:

...
// Open the reference page of the chosen API
chrome.omnibox.onInputEntered.addListener((input) => {
  chrome.tabs.create({ url: URL_CHROME_EXTENSIONS_DOC + input });
  // Save the latest keyword
  updateHistory(input);
});

تستخدِم الدالة updateHistory() مدخل المربع المتعدّد الاستخدامات وتحفظه في storage.local. بهذه الطريقة، يمكن استخدام أحدث عبارة بحث لاحقًا كاقتراح في المربّع المتعدد الاستخدامات.

sw-omnibox.js:

...
async function updateHistory(input) {
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  apiSuggestions.unshift(input);
  apiSuggestions.splice(NUMBER_OF_PREVIOUS_SEARCHES);
  return chrome.storage.local.set({ apiSuggestions });
}

الخطوة 6: إعداد حدث متكرّر

تُستخدَم الطريقتان setTimeout() أو setInterval() عادةً لتنفيذ المهام التي يتم تنفيذها بعد فترة أو بشكل دوري. ومع ذلك، يمكن أن تتعذّر هذه واجهات برمجة التطبيقات لأنّ المخطِّط سيلغي الموقّتات عند إنهاء الخدمة العملية. بدلاً من ذلك، يمكن للإضافات استخدام واجهة برمجة التطبيقات chrome.alarms.

يمكنك البدء بطلب إذن ""alarms"" في ملف البيان. بالإضافة إلى ذلك، لجلب نصائح الإضافات من موقع مستضاف عن بُعد، عليك طلب إذن المضيف:

manifest.json:

{
  ...
  "permissions": ["storage"],
  "permissions": ["storage", "alarms"],
  "host_permissions": ["https://extension-tips.glitch.me/*"],
}

ستسترجع الإضافة جميع النصائح، وستختار نصيحة عشوائية وتحفظها في مساحة التخزين. وسنُنشئ تنبيهًا لتشغيله مرة واحدة في اليوم لتعديل النصيحة. لا يتم حفظ الإنذارات عند إغلاق Chrome. لذلك، علينا التحقّق مما إذا كان المنبّه متوفّرًا وإنشاؤه إذا لم يكن كذلك.

sw-tips.js:

// Fetch tip & save in storage
const updateTip = async () => {
  const response = await fetch('https://extension-tips.glitch.me/tips.json');
  const tips = await response.json();
  const randomIndex = Math.floor(Math.random() * tips.length);
  return chrome.storage.local.set({ tip: tips[randomIndex] });
};

const ALARM_NAME = 'tip';

// Check if alarm exists to avoid resetting the timer.
// The alarm might be removed when the browser session restarts.
async function createAlarm() {
  const alarm = await chrome.alarms.get(ALARM_NAME);
  if (typeof alarm === 'undefined') {
    chrome.alarms.create(ALARM_NAME, {
      delayInMinutes: 1,
      periodInMinutes: 1440
    });
    updateTip();
  }
}

createAlarm();

// Update tip once a day
chrome.alarms.onAlarm.addListener(updateTip);

الخطوة 7: التواصل مع السياقات الأخرى

تستخدِم الإضافات نصوص برمجية للمحتوى لقراءة محتوى الصفحة وتعديله. عندما يزور أحد المستخدمين صفحة مرجعية لواجهة برمجة تطبيقات Chrome، سيعمل النص البرمجي لمحتوى الإضافة على تحديث الصفحة بنصيحة اليوم. يُرسِل رسالة لطلب معلومات اليوم من عامل الخدمة.

ابدأ بتعريف نص المحتوى في البيان وأضِف نمط المطابقة المقابل للمستندات المرجعية لواجهة برمجة التطبيقات Chrome API.

manifest.json:

{
  ...
  "content_scripts": [
    {
      "matches": ["https://developer.chrome.com/docs/extensions/reference/*"],
      "js": ["content.js"]
    }
  ]
}

أنشئ ملف محتوى جديدًا. ويرسل الرمز التالي رسالة إلى عامل الخدمة يطلب النصيحة. بعد ذلك، تتم إضافة زر يفتح نافذة منبثقة تحتوي على نصيحة حول الإضافة. يستخدم هذا الرمز البرمجي منصة الويب الجديدة Popover API.

content.js:

(async () => {
  // Sends a message to the service worker and receives a tip in response
  const { tip } = await chrome.runtime.sendMessage({ greeting: 'tip' });

  const nav = document.querySelector('.upper-tabs > nav');
  
  const tipWidget = createDomElement(`
    <button type="button" popovertarget="tip-popover" popovertargetaction="show" style="padding: 0 12px; height: 36px;">
      <span style="display: block; font: var(--devsite-link-font,500 14px/20px var(--devsite-primary-font-family));">Tip</span>
    </button>
  `);

  const popover = createDomElement(
    `<div id='tip-popover' popover style="margin: auto;">${tip}</div>`
  );

  document.body.append(popover);
  nav.append(tipWidget);
})();

function createDomElement(html) {
  const dom = new DOMParser().parseFromString(html, 'text/html');
  return dom.body.firstElementChild;
}

الخطوة الأخيرة هي إضافة معالِج رسائل إلى الخدمة العاملة التي تُرسِل ردًا على نص المحتوى يتضمّن النصيحة اليومية.

sw-tips.js:

...
// Send tip to content script via messaging
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.greeting === 'tip') {
    chrome.storage.local.get('tip').then(sendResponse);
    return true;
  }
});

اختبار صحة الإجراء

تأكَّد من أنّ بنية ملف مشروعك تظهر على النحو التالي:

محتوى مجلد الإضافة: مجلد &quot;الصور&quot; وmanifest.json وservice-worker.js وsw-omnibox.js وsw-tips.js
وcontent.js

تحميل الإضافة محليًا

لتحميل إضافة غير مضغوطة في وضع المطوّر، اتّبِع الخطوات الواردة في مرحبًا بك.

فتح صفحة مرجعية

  1. أدخِل الكلمة الرئيسية "api" في شريط عناوين المتصفّح.
  2. اضغط على "مفتاح التبويب" (Tab) أو "مفتاح المسافة".
  3. أدخِل الاسم الكامل لواجهة برمجة التطبيقات.
    • أو الاختيار من قائمة بعمليات البحث السابقة
  4. ستفتح صفحة جديدة تنقل إلى صفحة مرجع Chrome API.

من المفترض أن يظهر الرمز على النحو التالي:

مرجع Quick API الذي يفتح مرجع واجهة برمجة التطبيقات لوقت التشغيل
إضافة Quick API التي تفتح واجهة Runtime API

فتح نصيحة اليوم

انقر على زر "ملاحظة" في شريط التنقّل لفتح ملاحظة الإضافة.

فتح النصيحة اليومية في
إضافة Quick API لعرض نصيحة اليوم

🎯 التحسينات المحتملة

استنادًا إلى ما تعلمته اليوم، حاوِل تنفيذ أيّ من الإجراءات التالية:

  • تعرَّف على طريقة أخرى لتنفيذ اقتراحات مربّع البحث الشامل.
  • أنشئ نافذة منبثقة مخصّصة لعرض نصيحة الإضافة.
  • فتح صفحة إضافية تنقل إلى صفحات واجهة برمجة التطبيقات المرجعية لإضافات الويب في MDN

طاب يومك.

تهانينا على إكمال هذا الدليل التعليمي 🎉. يمكنك مواصلة تحسين مهاراتك من خلال إكمال أدلّة تعليمية أخرى للمبتدئين:

الإضافة ما سوف تتعلمه
وقت القراءة لإدراج عنصر في مجموعة محددة من الصفحات تلقائيًا.
مدير علامات التبويب لإنشاء نافذة منبثقة تدير علامات التبويب في المتصفّح
وضع التركيز لتشغيل الرمز على الصفحة الحالية بعد النقر على إجراء الإضافة.

الاطّلاع على معلومات إضافية

لمواصلة مسار التعلّم الخاص بعامل خدمة التوسيع، ننصحك باستكشاف المقالات التالية: