رویدادها را با کارگران خدمات اداره کنید

آموزشی که مفاهیم مربوط به کارکنان خدمات ترویجی را پوشش می‌دهد

نمای کلی

این آموزش مقدمه‌ای بر سرویس ورکرهای افزونه‌های کروم ارائه می‌دهد. به عنوان بخشی از این آموزش، افزونه‌ای خواهید ساخت که به کاربران امکان می‌دهد با استفاده از omnibox به سرعت به صفحات مرجع API کروم بروند. شما یاد خواهید گرفت که چگونه:

  • سرویس ورکر خود را ثبت کنید و ماژول‌ها را وارد کنید.
  • اشکال‌زدایی (debug) سرویس دهنده افزونه (extension service worker)
  • مدیریت وضعیت و مدیریت رویدادها.
  • رویدادهای دوره‌ای را فعال کنید.
  • با اسکریپت‌های محتوا ارتباط برقرار کنید.

قبل از شروع

این راهنما فرض می‌کند که شما تجربه اولیه توسعه وب را دارید. توصیه می‌کنیم برای آشنایی با توسعه افزونه‌ها، دوره‌های Extensions 101 و Hello World را مطالعه کنید.

ساخت افزونه

با ایجاد یک دایرکتوری جدید به نام quick-api-reference برای نگهداری فایل‌های افزونه شروع کنید، یا کد منبع را از مخزن نمونه‌های GitHub ما دانلود کنید.

مرحله ۱: ثبت سرویس ورکر

فایل 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"
  }
}

افزونه‌ها سرویس ورکر خود را در مانیفست ثبت می‌کنند که فقط یک فایل جاوا اسکریپت می‌گیرد. نیازی به فراخوانی navigator.serviceWorker.register() مانند آنچه در یک صفحه وب انجام می‌دهید، نیست.

یک پوشه images ایجاد کنید و سپس آیکون‌ها را در آن دانلود کنید .

برای کسب اطلاعات بیشتر در مورد متادیتا و آیکون‌های افزونه در مانیفست، مراحل اولیه آموزش زمان مطالعه را بررسی کنید.

مرحله ۲: وارد کردن چندین ماژول سرویس ورکر

سرویس ورکر ما دو ویژگی را پیاده‌سازی می‌کند. برای قابلیت نگهداری بهتر، هر ویژگی را در یک ماژول جداگانه پیاده‌سازی خواهیم کرد. ابتدا، باید سرویس ورکر را به عنوان یک ماژول ES در مانیفست خود اعلام کنیم که به ما امکان می‌دهد ماژول‌ها را در سرویس ورکر خود وارد کنیم:

مانیفست.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 ثانیه عبارت "service worker (inactive)" را خواهید دید که به معنی خاتمه یافتن service worker است. برای بررسی آن، روی لینک "service worker (inactive)" کلیک کنید. انیمیشن زیر این موضوع را نشان می‌دهد.

آیا متوجه شدید که بررسی سرویس ورکر آن را فعال کرده است؟ باز کردن سرویس ورکر در devtools آن را فعال نگه می‌دارد. برای اطمینان از اینکه افزونه شما هنگام خاتمه سرویس ورکر به درستی رفتار می‌کند، به یاد داشته باشید که DevTools را ببندید.

حالا، افزونه را غیرفعال کنید تا یاد بگیرید کجا خطاها را پیدا کنید. یک راه برای انجام این کار، حذف ".js" از فایل './sw-omnibox.js' در فایل service-worker.js است. کروم قادر به ثبت سرویس ورکر نخواهد بود.

به chrome://extensions برگردید و افزونه را رفرش کنید. دو خطا مشاهده خواهید کرد:

Service worker registration failed. Status code: 3.

An unknown error occurred when fetching the script.

برای روش‌های بیشتر برای اشکال‌زدایی از سرویس‌دهنده‌ی افزونه، به بخش اشکال‌زدایی افزونه‌ها مراجعه کنید.

مرحله ۴: مقداردهی اولیه حالت

کروم در صورت عدم نیاز به سرویس ورکرها، آنها را خاموش می‌کند. ما از API مربوط به chrome.storage برای حفظ وضعیت در جلسات سرویس ورکرها استفاده می‌کنیم. برای دسترسی به فضای ذخیره‌سازی، باید در مانیفست درخواست مجوز کنیم:

مانیفست.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 استفاده کنید که داده‌ها را در دستگاه محلی ذخیره می‌کند.

برای آشنایی با سایر گزینه‌های ذخیره‌سازی برای کارکنان خدمات توسعه، به بخش «داده‌های ماندگار» به جای استفاده از متغیرهای سراسری مراجعه کنید.

مرحله ۵: رویدادهای خود را ثبت کنید

همه شنونده‌های رویداد باید به صورت ایستا در محدوده سراسری سرویس ورکر ثبت شوند. به عبارت دیگر، شنونده‌های رویداد نباید در توابع ناهمگام تو در تو باشند. به این ترتیب کروم می‌تواند اطمینان حاصل کند که در صورت راه‌اندازی مجدد سرویس ورکر، همه کنترل‌کننده‌های رویداد بازیابی می‌شوند.

در این مثال، ما قصد داریم از API chrome.omnibox استفاده کنیم، اما ابتدا باید کلمه کلیدی trigger مربوط به omnibox را در مانیفست تعریف کنیم:

مانیفست.json:

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

حالا، شنونده‌های رویداد omnibox را در سطح بالای اسکریپت ثبت کنید. وقتی کاربر کلمه کلیدی omnibox ( api ) را در نوار آدرس وارد می‌کند و به دنبال آن tab یا space می‌گذارد، کروم لیستی از پیشنهادات را بر اساس کلمات کلیدی موجود در حافظه نمایش می‌دهد. رویداد 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() صفحه مرجع API کروم مربوطه را باز می‌کند.

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() ورودی omnibox را دریافت کرده و آن را در storage.local ذخیره می‌کند. به این ترتیب جدیدترین عبارت جستجو می‌تواند بعداً به عنوان پیشنهاد omnibox استفاده شود.

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

مرحله ۶: یک رویداد تکرارشونده تنظیم کنید

متدهای setTimeout() یا setInterval() معمولاً برای انجام وظایف با تأخیر یا دوره‌ای استفاده می‌شوند. با این حال، این APIها می‌توانند با شکست مواجه شوند زیرا برنامه‌ریز هنگام خاتمه یافتن سرویس ورکر، تایمرها را لغو می‌کند. در عوض، افزونه‌ها می‌توانند از API chrome.alarms استفاده کنند.

با درخواست مجوز "alarms" در مانیفست شروع کنید:

مانیفست.json:

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

این افزونه تمام نکات را دریافت می‌کند، یکی را به صورت تصادفی انتخاب کرده و در حافظه ذخیره می‌کند. ما یک زنگ هشدار ایجاد خواهیم کرد که روزی یک بار برای به‌روزرسانی نکات فعال می‌شود. وقتی کروم را می‌بندید، هشدارها ذخیره نمی‌شوند. بنابراین باید بررسی کنیم که آیا زنگ هشدار وجود دارد یا خیر و در صورت عدم وجود، آن را ایجاد کنیم.

sw-tips.js:

// Fetch tip & save in storage
const updateTip = async () => {
  const response = await fetch('https://chrome.dev/f/extension_tips/');
  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);

مرحله ۷: با سایر زمینه‌ها ارتباط برقرار کنید

افزونه‌ها از اسکریپت‌های محتوا برای خواندن و تغییر محتوای صفحه استفاده می‌کنند. وقتی کاربری از صفحه مرجع API کروم بازدید می‌کند، اسکریپت محتوای افزونه، صفحه را با نکته روز به‌روزرسانی می‌کند. این اسکریپت پیامی برای درخواست نکته روز از سرویس ورکر ارسال می‌کند .

با اعلان اسکریپت محتوا در مانیفست شروع کنید و الگوی تطبیق مربوط به مستندات مرجع API کروم را اضافه کنید.

مانیفست.json:

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

یک فایل محتوای جدید ایجاد کنید. کد زیر پیامی را به سرویس ورکر ارسال می‌کند که درخواست دریافت نکته را دارد. سپس، دکمه‌ای اضافه می‌کند که یک پنجره پاپ‌اوور حاوی نکته افزونه را باز می‌کند. این کد از API جدید پلتفرم وب Popover استفاده می‌کند.

محتوای.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;
  }
});

تست کنید که کار می‌کند

بررسی کنید که ساختار فایل‌های پروژه شما به شکل زیر باشد:

محتویات پوشه افزونه: پوشه images، manifest.json، service-worker.js، sw-omnibox.js، sw-tips.js و content.js

افزونه خود را به صورت محلی بارگذاری کنید

برای بارگذاری یک افزونه‌ی باز نشده در حالت توسعه‌دهنده، مراحل موجود در Hello world را دنبال کنید.

باز کردن صفحه مرجع

  1. کلمه کلیدی "api" را در نوار آدرس مرورگر وارد کنید.
  2. دکمه‌ی «tab» یا «space» را فشار دهید.
  3. نام کامل API را وارد کنید.
    • یا از لیست جستجوهای گذشته انتخاب کنید
  4. یک صفحه جدید به صفحه مرجع API کروم باز خواهد شد.

باید به این شکل باشد:

مرجع سریع API باز کردن مرجع API زمان اجرا
افزونه‌ی Quick API که Runtime API را باز می‌کند.

نوک روز را باز کنید

برای باز کردن افزونه‌ی نکته‌دار، روی دکمه‌ی نکته (Tip) که در نوار ناوبری قرار دارد کلیک کنید.

نوک روزانه را باز کنید
افزونه سریع API، نکته روز را باز می‌کند.

🎯 پیشرفت‌های بالقوه

بر اساس آنچه امروز آموخته‌اید، سعی کنید هر یک از موارد زیر را انجام دهید:

  • روش دیگری را برای پیاده‌سازی پیشنهادات omnibox بررسی کنید.
  • برای نمایش نکته افزونه، مودال سفارشی خود را ایجاد کنید.
  • یک صفحه اضافی به صفحات API مرجع افزونه‌های وب MDN باز کنید.

به ساختن ادامه بده!

تبریک می‌گویم که این آموزش را تمام کردید 🎉. با تکمیل سایر آموزش‌های مقدماتی، مهارت‌های خود را ارتقا دهید:

پسوند آنچه یاد خواهید گرفت
زمان خواندن برای درج خودکار یک عنصر در مجموعه‌ای خاص از صفحات.
مدیریت تب‌ها برای ایجاد یک پنجره بازشو که تب‌های مرورگر را مدیریت می‌کند.
حالت فوکوس برای اجرای کد در صفحه فعلی پس از کلیک روی اکشن افزونه.

ادامه کاوش

برای ادامه مسیر یادگیری خود به عنوان یک کارمند خدمات ترویجی، توصیه می‌کنیم مقالات زیر را مطالعه کنید: