תארו לעצמכם שבניתם תוסף Chrome—הוא עובד ללא רבב. ואז אתם מעבירים אותו ל-Firefox, וחצי מה-API-ים מתנהגים אחרת. בפרויקט אגרגטור פיננסי, browser.storage.local ב-Firefox החזיר undefined ללא שגיאה, מה שגרם לאיפוס כל הגדרות המשתמש. נאלצנו לשכתב 40% מהקוד. כל דפדפן מוסיף יומיים נוספים של ניפוי שגיאות. פיתחנו גישה שיטתית: קוד בסיס אחיד, WebExtension API, polyfills ובנייה מותנית. קוד בסיס אחיד מקצץ את התקציב ב-40–60% בהשוואה לפיתוח תוספים נפרדים לכל דפדפן. חיסכון בזמן של פי 3-4 מתורגם להפחתת תקציב של 30–50%. המחיר נקבע לאחר ניתוח מעמיק של הדרישות שלכם.
כיצד אנו פותרים את בעיית תאימות ה-API
ה-webextension-polyfill
מוזילה פיתחה את webextension-polyfill—הוא ממיר chrome.* (callback) ל-browser.* (Promise) ומחליק הבדלים בין דפדפנים. לפי מפרט WebExtensions, ההתקנה היא פקודה אחת:
npm install webextension-polyfill import browser from 'webextension-polyfill';
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
await browser.storage.local.set({ data: 'value' });
const result = await browser.storage.local.get('data');
ה-polyfill מקצר את זמן ההתאמה פי 3-4 בהשוואה לעטיפה ידנית—הוכח בפרויקטים פיננסיים ובפרויקטים של מסחר אלקטרוני.
תאימות API לדפדפנים
| API | Chrome | Firefox | Edge | Opera | Safari |
|---|---|---|---|---|---|
npm install webextension-polyfill |
MV2/3 | MV2/3 | MV2/3 | MV2/3 | 14+ |
import browser from 'webextension-polyfill'; const tabs = await browser.tabs.query({ active: true, currentWindow: true }); await browser.storage.local.set({ data: 'value' }); const result = await browser.storage.local.get('data'); |
✓ | ✓ | ✓ | ✓ | 15+ |
storage.local |
✓ | ✓ | ✓ | ✓ | 14+ |
storage.sync (MV3) |
✓ | 101+ | ✓ | 92+ | 15.4+ |
tabs |
✓ | 113+ | ✓ | 92+ | 15.4+ |
scripting |
114+ | — | — | — | — |
declarativeNetRequest |
MV2 בלבד | ✓ | MV2 בלבד | MV2 בלבד | — |
קיימים הבדלים—לדוגמה, sidePanel זמין רק ב-Chrome 114+. למקרים כאלה, אנו משתמשים בנתיבים מותנים ובמימושים חלופיים. העיקרון: ה-polyfill מכסה 90% מהפעולות הטיפוסיות; השאר הוא התאמה ידנית, שהיא אמינה יותר מהרחבת עטיפות.
מדוע אנו משתמשים במניפסטים נפרדים?
Chrome עם MV3 דורש service_worker, Firefox עם MV2 משתמש ב-webRequest. שמירה על שני המניפסטים במקביל היא נוהג סטנדרטי לכיסוי מקסימלי. אנו בונים את המניפסט הסופי באמצעות סקריפט מ-sidePanel בתוספת הגדרות ספציפיות לכל דפדפן:
// scripts/build.js
const fs = require('fs');
const target = process.env.TARGET_BROWSER;
const manifests = {
chrome: {
manifest_version: 3,
background: {
service_worker: 'background.js'
},
action: {
default_popup: 'popup.html'
},
},
firefox: {
manifest_version: 2,
background: {
scripts: ['background.js'],
persistent: false
},
browser_action: {
default_popup: 'popup.html'
},
browser_specific_settings: {
gecko: {
id: '[email protected]',
strict_min_version: '109.0'
},
},
},
safari: {
manifest_version: 3,
background: {
service_worker: 'background.js'
},
action: {
default_popup: 'popup.html'
},
browser_specific_settings: {
safari: {
strict_min_version: '15.4'
},
},
},
};
const base = JSON.parse(fs.readFileSync('./manifest.base.json', 'utf8'));
const merged = {
...base,
...manifests[target]
};
fs.writeFileSync('./dist/manifest.json', JSON.stringify(merged, null, 2)); השוואת מניפסט MV2 לעומת MV3
| היבט | MV2 | MV3 |
|---|---|---|
| רקע | background.scripts + Browser Action | Service Worker + Action |
API manifest.base.json | תמיכה מלאה | רק declarativeNetRequest |
API // scripts/build.js const fs = require('fs'); const target = process.env.TARGET_BROWSER; const manifests = { chrome: { manifest_version: 3, background: { service_worker: 'background.js' }, action: { default_popup: 'popup.html' }, }, firefox: { manifest_version: 2, background: { scripts: ['background.js'], persistent: false }, browser_action: { default_popup: 'popup.html' }, browser_specific_settings: { gecko: { id: '[email protected]', strict_min_version: '109.0' }, }, }, safari: { manifest_version: 3, background: { service_worker: 'background.js' }, action: { default_popup: 'popup.html' }, browser_specific_settings: { safari: { strict_min_version: '15.4' }, }, }, }; const base = JSON.parse(fs.readFileSync('./manifest.base.json', 'utf8')); const merged = { ...base, ...manifests[target] }; fs.writeFileSync('./dist/manifest.json', JSON.stringify(merged, null, 2)); | מלא | מוגבל (לא ב-service worker) |
| אבטחה | ביצוע קוד שרירותי | eval() אסור, סקריפטים מרוחקים מוגבלים |
| התקנה | ידנית או אוטומטית | דורש חתימה ועדכונים דרך החנות |
בחירת MV2 או MV3 תלויה בדפדפני היעד ובפונקציונליות. לדוגמה, תוסף שעושה שימוש נרחב ב-background.scripts עדיין דורש MV2 ב-Chrome, אם כי Firefox כבר עובד עם MV3 דרך declarativeNetRequest. אנו עוזרים לקבוע את האסטרטגיה האופטימלית.
כיצד להימנע משגיאות העברת הודעות?
הקלדה קפדנית של הודעות בין הקשרים מונעת באגים שקטים. אנו משתמשים ב-discriminated union ב-TypeScript:
// shared/messages.ts
export type Message =
| { type: 'GET_PAGE_DATA'; url: string }
| { type: 'SET_BADGE'; count: number; color?: string }
| { type: 'OPEN_OPTIONS' }
| { type: 'EXTRACT_TEXT'; selector: string };
export type MessageResponse<T extends Message> = T extends { type: 'GET_PAGE_DATA' }
? { title: string; meta: Record<string, string> }
: T extends { type: 'EXTRACT_TEXT' }
? { text: string }
: void;
export async function sendMessage<T extends Message>(
message: T
): Promise<MessageResponse<T>> {
return browser.runtime.sendMessage(message) as Promise<MessageResponse<T>>;
}
export function onMessage<T extends Message['type']>(
type: T,
handler: (
msg: Extract<Message, { type: T }>,
sender: browser.runtime.MessageSender
) => Promise<MessageResponse<Extract<Message, { type: T }>>>
) {
browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === type) {
handler(msg, sender).then(sendResponse);
return true;
}
});
}
גישה זו מבטיחה שהמטפל מקבל בדיוק את סוג ההודעה הצפוי. המהדר לא יאפשר אי-התאמות. הודעות מוקלדות הן סטנדרט בכל הפרויקטים שלנו, כולל תוספים פיננסיים ותוספי מסחר אלקטרוני עם מאות אלפי משתמשים.
מה כלול בעבודה
אנו מספקים פתרון סוהר:
- ארכיטקטורה ועיצוב—בחירת מחסנית טכנולוגית, הקלדה, מבנה קבצים.
- מימוש ובדיקות—קידוד, בדיקות יחידה, בדיקות E2E עם Playwright.
- בנייה ו-CI/CD—הגדרת Vite, סקריפטי בנייה, GitHub Actions לפרסום.
- תיעוד—README, תיאור API, הוראות בנייה.
- פרסום—העלאה ל-Chrome Web Store, Firefox AMO, Edge Add-ons, Opera Addons (Safari לפי בקשה).
- תמיכה—תקופת אחריות של 30 יום לאחר המסירה, תיקוני באגים, התאמה לגרסאות דפדפן חדשות.
יש לנו ניסיון של למעלה מ-5 שנים ו-30+ פרויקטים מוצלחים במגזרים הפיננסיים, מסחר אלקטרוני ו-SaaS. צרו קשר כדי להעריך את הפרויקט שלכם—נעזור לבחור את הארכיטקטורה האופטימלית.
לוח זמנים
תוסף דפדפן חוצה-דפדפנים (Chrome + Firefox + Edge) עם קוד בסיס אחיד, הקלדה, בנייה אוטומטית ופרסום לשלוש חנויות לוקח 10–16 ימי עבודה בהתאם לפונקציונליות. הוספת Safari (עטיפת Xcode + App Store) מוסיפה 5–8 ימים. זמני בדיקת החנויות הם נפרדים ומקבילים.
הזמינו פיתוח תוסף דפדפן חוצה-דפדפנים—נעריך את המשימה שלכם תוך יום עבודה אחד. קבלו ייעוץ בטלפון או בצ'אט כדי לדון בפונקציונליות ובלוח הזמנים.







