🌐 Detecting your location…

كيفية إنشاء ملحق Chrome باستخدام Manifest V3 في عام 2026: الدليل الكامل

⏱️3 min read  ·  485 words

أصبح Manifest V3 الآن هو الخيار الوحيد لإضافات Chrome الجديدة، وهو يغير البنية بشكل مفيد: أصبحت صفحات الخلفية عمال خدمة سريعة الزوال، وتم حظر تنفيذ التعليمات البرمجية عن بعد، وتم نقل اعتراض الشبكة إلى واجهة برمجة التطبيقات التعريفية. إذا تعلمت تطوير الامتدادات على الإصدار الثاني، فلن تعمل العديد من العادات. ينشئ هذا الدليل امتدادًا كاملاً وعمليًا من البداية بموجب قواعد V3.

ما نقوم ببنائه

ملحق التعليق التوضيحي للصفحة: يضيف نافذة منبثقة لشريط الأدوات، ويدخل نصًا برمجيًا للمحتوى يبرز النص المحدد في أي صفحة، ويحفظ التمييزات لكل عنوان URL، ومزامنتها من خلالchrome.storage. إنه يتدرب على كل جزء من V3 الذي ستستخدمه بالفعل – واجهة المستخدم المنبثقة، والبرنامج النصي للمحتوى، وعامل الخدمة، والمراسلة، والتخزين، والأذونات.

الخطوة 1: هيكل المشروع والبيان

قم بإنشاء دليل بهذه الملفات. البيان هو نقطة الدخول ويقرأه Chrome أولاً.

my-extension/
  manifest.json
  background.js
  content.js
  popup.html
  popup.js
  styles.css
  icons/icon16.png icon48.png icon128.png

يعلن البيان عن الإصدار 3 ونقاط الدخول والأذونات الخاصة بك. حافظ على الحد الأدنى من الأذونات – كل إذن إضافي يؤدي إلى إبطاء المراجعة وإخافة المستخدمين في وقت التثبيت.

{
  "manifest_version": 3,
  "name": "Page Highlighter",
  "version": "1.0.0",
  "description": "Highlight and save text on any page.",
  "permissions": ["storage", "activeTab", "scripting"],
  "host_permissions": ["http://*/*", "https://*/*"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "content_scripts": [
    {
      "matches": ["http://*/*", "https://*/*"],
      "js": ["content.js"],
      "css": ["styles.css"],
      "run_at": "document_idle"
    }
  ],
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

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

الخطوة 2: عامل الخدمة ليس صفحة خلفية

هذا هو أكبر تغيير V3. النص البرمجي للخلفية هو عامل خدمة ينهيه Chrome عندما يكون خاملاً ويعاد تشغيله في الحدث التالي. أي متغير قمت بتعيينه في المستوى الأعلى سيختفي بعد الإنهاء. يجب أن تعيش الدولة فيchrome.storage، وليس في الذاكرة.

// background.js

// WRONG under V3 — this resets every time the worker restarts.
// let highlightCount = 0;

chrome.runtime.onInstalled.addListener(async () => {
  const { highlights } = await chrome.storage.local.get('highlights');
  if (!highlights) {
    await chrome.storage.local.set({ highlights: {} });
  }
});

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'SAVE_HIGHLIGHT') {
    saveHighlight(message.payload).then(() => sendResponse({ ok: true }));
    // Returning true keeps the message channel open for the async reply.
    return true;
  }
});

async function saveHighlight({ url, text }) {
  const { highlights = {} } = await chrome.storage.local.get('highlights');
  const forPage = highlights[url] || [];
  forPage.push({ text, createdAt: Date.now() });
  highlights[url] = forPage;
  await chrome.storage.local.set({ highlights });
}

من السهل تفويت مستمع الرسالة ويسبب خطأً كلاسيكيًا: يكتمل معالج المزامنة ولكن المرسل لا يتلقى الرد أبدًا، لأن Chrome أغلق القناة عندما عاد المستمع غير محدد.return trueالخطوة 3: البرنامج النصي للمحتوى

يتم تشغيل البرامج النصية للمحتوى في DOM الخاص بالصفحة ولكن في عالم JavaScript معزول. يمكنهم قراءة DOM وتعديله، لكن لا يمكنهم رؤية متغيرات JavaScript الخاصة بالصفحة. يعد هذا العزل ميزة أمان، وليس قيدًا على الحل البديل.

لاحظ

// content.js
document.addEventListener('mouseup', async () => {
  const selection = window.getSelection();
  const text = selection.toString().trim();
  if (text.length < 3) return;

  const range = selection.getRangeAt(0);
  const mark = document.createElement('mark');
  mark.className = 'ext-highlight';

  try {
    range.surroundContents(mark);
  } catch {
    // surroundContents throws when the selection crosses element boundaries.
    return;
  }

  await chrome.runtime.sendMessage({
    type: 'SAVE_HIGHLIGHT',
    payload: { url: location.href, text }
  });

  selection.removeAllRanges();
});

حولtry/catch. يتم طرحه عندما يمتد التحديد إلى عناصر متعددة، وهو ما يحدث باستمرار في الصفحات الحقيقية. إن التعامل معها هو الفرق بين الامتداد الذي يعمل على صفحتك الاختبارية والامتداد الذي يعمل في كل مكان.surroundContentsالخطوة 4: النافذة المنبثقة

النافذة المنبثقة عبارة عن صفحة ويب عادية تتمتع بإمكانية الوصول إلى واجهات برمجة التطبيقات (APIs) الملحقة. يتم إتلافه في كل مرة يتم إغلاقه، لذا تعامل معه على أنه عديم الحالة واقرأه من التخزين عند الفتح.

يتم حظر البرامج النصية المضمنة بواسطة سياسة أمان محتوى الامتداد، لذا فإن

<!-- popup.html -->
<!DOCTYPE html>
<html>
  <head><meta charset="utf-8"></head>
  <body style="width:320px;font:14px system-ui;padding:12px">
    <h1 style="font-size:15px;margin:0 0 8px">Highlights on this page</h1>
    <ul id="list"></ul>
    <script src="popup.js"></script>
  </body>
</html>

المرجع إلزامي — لا يمكنك وضع JavaScript مباشرة في HTML.<script src>استخدم

// popup.js
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const { highlights = {} } = await chrome.storage.local.get('highlights');
const items = highlights[tab.url] || [];

const list = document.getElementById('list');
if (items.length === 0) {
  list.innerHTML = '<li>No highlights yet.</li>';
} else {
  for (const item of items) {
    const li = document.createElement('li');
    li.textContent = item.text;   // textContent, never innerHTML
    list.appendChild(li);
  }
}

بدلاً منtextContent لأي قيمة نشأت من صفحة ويب. النص المميز عبارة عن بيانات يتحكم فيها المهاجم، ويعد إنشاء HTML منها بمثابة مسار XSS مباشر إلى السياق المميز لامتدادك.innerHTMLالخطوة 5: خيارات التخزين

يمنحك Chrome ثلاث مناطق تخزين ويؤدي الاختيار الخاطئ إلى حدوث فشل صامت.

المنطقة

الحصة استخدم لـ ~10 ميجابايت (غير محدود بإذن)
storage.local بيانات مجمعة، محتوى مخبأ إجمالي 100 كيلو بايت تقريبًا، 8 كيلو بايت لكل عنصر
storage.sync إعدادات مستخدم صغيرة، متزامنة عبر الأجهزة ~10 ميجابايت، الذاكرة فقط
storage.session البيانات التي لا ينبغي أن تنجو من إعادة تشغيل المتصفح الخطأ الشائع هو وضع محتوى المستخدم في

لأن المزامنة تبدو مرغوبة. يتم الوصول إلى الحد الأقصى البالغ 8 كيلو بايت لكل عنصر بسرعة وتتم الكتابة ثم تفشل – غالبًا بصمت، إذا لم تتحقق من وجود أخطاء.storage.syncالخطوة 6: تم تغيير اعتراض الشبكة

الحجب

اختفت واجهة برمجة التطبيقات. إذا كنت بحاجة إلى حظر الطلبات أو إعادة توجيهها، فاستخدمwebRequest، حيث تقوم بتسجيل القواعد الثابتة التي يقوم Chrome بتقييمها بنفسه. لا ترى الإضافة الطلب مطلقًا.declarativeNetRequestيعد هذا أكثر تقييدًا من حيث التصميم، ولهذا السبب كان من الضروري إعادة كتابة العديد من أدوات حظر الإعلانات. إذا كانت القيمة الأساسية لامتدادك تعتمد على فحص نصوص الطلب في وقت التشغيل، فقد لا يدعمها V3 حقًا.

{
  "permissions": ["declarativeNetRequest"],
  "declarative_net_request": {
    "rule_resources": [{
      "id": "ruleset_1",
      "enabled": true,
      "path": "rules.json"
    }]
  }
}

الخطوة 7: تحميل وتصحيح

فتح

، قم بتمكين وضع المطور، ثم اختر “تحميل غير مضغوط”. توجد ثلاث وحدات تحكم منفصلة ومعرفة أي منها سيتم فتحه يوفر ساعات:chrome://extensionsعامل الخدمة:

  • انقر فوق رابط “عامل الخدمة” الموجود على بطاقة التمديدالمنبثقة:
  • انقر بزر الماوس الأيمن فوق النافذة المنبثقة واختر فحصنص المحتوى:
  • وحدة تحكم DevTools للصفحة العادية، مع تعيين محدد السياق على الامتدادإذا بدا عامل الخدمة ميتًا، فهذا أمر متوقع — وينتهي بعد 30 ثانية تقريبًا من عدم النشاط. إطلاق حدث وإعادة تشغيله.

الخطوة 8: النشر

الخطوة 8: النشر

قم بضغط محتويات دليل الامتداد (وليس المجلد المرفق) وقم بالتحميل من خلال لوحة تحكم مطوري سوق Chrome الإلكتروني، الأمر الذي يتطلب رسوم تسجيل لمرة واحدة. يعتمد وقت المراجعة بشكل كبير على أذوناتك: ملحق يستخدمactiveTabفقط وstorage عادةً ما يقوم بمسح المراجعة بسرعة، في حين أن أذونات المضيف واسعة بالإضافة إلىscripting جذب المراجعة اليدوية ويمكن أن يستغرق وقتًا أطول بكثير. اكتب مبررًا واضحًا لكل إذن في القائمة – يرفض المراجعون التفسيرات الغامضة.

أخطاء شائعة

تخزين الحالة في عوالم عامل الخدمة. يختفي عند الإنهاء. استخدمchrome.storage.

النسيانreturn true في مستمعي الرسائل غير المتزامنة. الرد لا يأتي أبدا والفشل صامت.

طلب<all_urls> متىactiveTab سوف تفعل. إنه يبطئ عملية المراجعة ويقلل عمليات التثبيت.

جارٍ تحميل الرمز البعيد. V3 يحظر ذلك تمامًا. يجب أن يتم شحن جميع التعليمات البرمجية القابلة للتنفيذ في الحزمة، مما يعني عدم وجود نصوص CDN ولاeval.

الخلاصة

يأتي تطوير Manifest V3 في عدد قليل من التخصصات:تعامل مع عامل الخدمة على أنه عديم الجنسية واحتفظ بكل الحالة فيchrome.storage، اطلب أضيق الأذونات التي تعمل، استخدمtextContent لأي شيء مصدره الصفحة، تذكرreturn true لمعالجات الرسائل غير المتزامنة، وقم بشحن كل سطر من التعليمات البرمجية داخل الحزمة. قم بالبناء مع هذه القيود منذ البداية وتبقى المنصة بعيدة عن طريقك – حيث يكمن الألم في تعديلها على تصميم على شكل V2.

MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা