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

برنامج تعليمي يتناول مفاهيم مشغّلات خدمات الإضافات

نظرة عامة

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

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

قبل البدء

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

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

ابدأ بإنشاء دليل جديد يُسمى 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 API للحفاظ على الحالة في جميع جلسات مشغّلي الخدمات. للوصول إلى مساحة التخزين، نحتاج إلى طلب الإذن في ملف البيان:

manifest.json:

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

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

sw-omnibox.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: تسجيل الأحداث

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

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

manifest.json:

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

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

sw-omnibox.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.

sw-omnibox.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", "alarms"],
  "permissions": ["storage"],
  "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.

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;
  }
});

اختبار أنه يعمل

تحقق من أن بنية الملف لمشروعك تبدو كما يلي:

محتوى مجلد الإضافات: مجلّد الصور، وManifest.json، وservice-work.js، وsw- الآلي.js، وsw-tips.js،
وcontent.js

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

لتحميل إضافة تم فك حزمتها في وضع مطوّر البرامج، اتّبِع الخطوات الواردة في Hello world.

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

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

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

مرجع واجهة برمجة تطبيقات سريعة يفتح مرجع واجهة برمجة التطبيقات في وقت التشغيل
إضافة سريعة لواجهة برمجة التطبيقات تؤدي إلى فتح واجهة برمجة تطبيقات وقت التشغيل

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

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

فتح النصيحة اليومية في
إضافة سريعة لواجهة برمجة التطبيقات تفتح نصيحة اليوم.

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

بناءً على ما تعلمته اليوم، حاول تحقيق أي مما يلي:

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

استمر في البناء!

تهانينا على إكمال هذا البرنامج التعليمي 🎉. يمكنك متابعة الارتقاء بمهاراتك من خلال إكمال برامج تعليمية أخرى للمبتدئين:

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

مواصلة الاستكشاف

لمتابعة المسار التعليمي لمشغّل خدمات الإضافات، ننصحك بالاطّلاع على المقالات التالية: