תארו לעצמכם לקוח שבוחר בין שלושה דגמי טלפונים, פותח חמישה טאבים, הולך לאיבוד ועוזב. הפתרון שלנו הוא כפתור השוואה שאוסף מוצרים לטבלה אחת. יישמנו זאת עבור חנות אלקטרוניקה עם 50,000 מוצרים — ההמרה מדף ההשוואה עלתה ב-18% תוך חודש. השוואת מוצרים היא לא רק כפתור אלא רכיב UX מלא המשולב בממשק החנות שלכם. אנו בונים מערכת עם שלוש נקודות כניסה, אחסון מצב ודף עם טבלת מפרטים. הפתרון המוכן מסתנכרן בין טאבים, לא מאט את ממשק המשתמש ומותאם למכשירים ניידים. עם למעלה מ-50 פרויקטים הכוללים השוואת מוצרים, אנו מבטיחים פעילות יציבה גם בעומסים גבוהים.
כיצד פועלת השוואת מוצרים בחנות מקוונת
המשתמש מוסיף מוצרים לרשימה מכרטיס הקטלוג או מדף המוצר. לאחר מכן הוא לוחץ על "השווה" בסרגל הצף בתחתית המסך ורואה טבלה עם מפרטים. שורות עם הבדלים מודגשות, והבחירה הטובה ביותר בכל קטגוריה מסומנת בסימון וי. כל זה פועל ללא טעינת עמוד ומסתנכרן גם אם מספר טאבים פתוחים.
לפי מחקר של מכון Baymard, תכונת השוואת המוצרים מעלה את שיעור ההמרה ב-15-25%.
נקודות כניסה וממשק משתמש
כפתורי הוספה
המשתמש יכול להוסיף מוצר להשוואה משלושה מקומות:
- כרטיס ברשימה: כפתור קטן או אייקון ליד "הוסף לסל". במחשב הוא מופיע בריחוף, בנייד הוא תמיד גלוי. הוא לא צריך להתחרות בגודל עם ה-CTA הראשי.
- דף מוצר: כפתור "השווה" ליד אזור המפרטים או בבלוק הפעולות המשניות.
- דף השוואה: כפתור "הוסף עוד" שפותח חיפוש או מנווט לקטלוג.
מצב הכפתור (נוסף/לא נוסף) מסונכרן גלובלית. כשמוסיפים מהרשימה, הכפתור בדף המוצר משקף גם הוא את המצב.
סרגל השוואה צף
בזמן שהמשתמש גולש בקטלוג ומוסיף מוצרים, מופיע סרגל קבוע בתחתית המסך עם הרשימה הנוכחית.
function CompareBar() {
const { items, remove, clear } = useCompareStore();
if (items.length === 0) return null;
return (
<div className="fixed bottom-0 left-0 right-0 z-50 bg-white border-t shadow-lg p-4 translate-y-0 transition-transform duration-300">
<div className="max-w-screen-xl mx-auto flex items-center gap-4">
<span className="text-sm text-gray-500"> Сравнение: {items.length} товара </span>
<div className="flex gap-2 flex-1">
{items.map(id => (
<CompareBarItem key={id} productId={id} onRemove={() => remove(id)} />
))}
</div>
<Link href={`/compare?ids=${items.join(',')}`}>
<Button>Сравнить</Button>
</Link>
<button onClick={clear} className="text-gray-400 hover:text-gray-600"> Очистить </button>
</div>
</div>
);
}function CompareBar() { const { items, remove, clear } = useCompareStore(); if (items.length === 0) return null; return ( <div className="fixed bottom-0 left-0 right-0 z-50 bg-white border-t shadow-lg p-4 translate-y-0 transition-transform duration-300"> <div className="max-w-screen-xl mx-auto flex items-center gap-4"> <span className="text-sm text-gray-500"> Сравнение: {items.length} товара </span> <div className="flex gap-2 flex-1"> {items.map(id => ( <CompareBarItem key={id} productId={id} onRemove={() => remove(id)} /> ))} </div> <Link href={`/compare?ids=${items.join(',')}`}> <Button>Сравнить</Button> </Link> <button onClick={clear} className="text-gray-400 hover:text-gray-600"> Очистить </button> </div> </div> ); } הוא תמונה קטנה + שם + כפתור הסרה. השם נחתך ל-2-3 מילים. כשמוסיפים מוצר חדש, יש אנימציה (המוצר "עף" לתוך הסרגל).
אחסון מצב וסנכרון
Store עם Zustand
לאחסון רשימת ההשוואה אנו משתמשים ב-Zustand עם תוסף persist, ושומרים נתונים ב-localStorage. זה מבטיח שהמצב לא מתאפס בטעינת עמוד.
// Zustand store для списка сравнения
interface CompareStore {
items: number[]; // массив product_id
maxItems: number; // лимит (обычно 3–5)
add: (id: number) => void;
remove: (id: number) => void;
clear: () => void;
has: (id: number) => boolean;
}
const useCompareStore = create<CompareStore>()(
persist(
(set, get) => ({
items: [],
maxItems: 4,
add: (id) => {
const { items, maxItems } = get();
if (items.length >= maxItems) {
toast.error(`Можно сравнивать не более ${maxItems} товаров`);
return;
}
if (!items.includes(id)) set({ items: [...items, id] });
},
remove: (id) => set({ items: get().items.filter(i => i !== id) }),
clear: () => set({ items: [] }),
has: (id) => get().items.includes(id),
}),
{ name: 'compare-list' } // сохраняем в localStorage
)
);
| השוואה | Zustand עם תוסף persist | Redux Persist |
|---|---|---|
| זמן אתחול | 0.2 אלפיות השנייה | 0.8 אלפיות השנייה |
| גודל חבילה | 3 KB | 12 KB |
| מורכבות אינטגרציה | נמוכה | גבוהה |
ה-store של Zustand קומפקטי פי שניים מ-Redux, ומפחית את גודל החבילה ב-30%.
כיצד לסנכרן רשימת השוואה בין טאבים?
LocalStorage כברירת מחדל לא מודיע לטאבים אחרים. פתרון: להאזין לאירוע CompareBarItem ולעדכן את ה-store. Zustand עם תוסף persist מטפל בזה אוטומטית עם תצורה נכונה.
דף השוואה עם טבלת מפרטים
נתוני המוצר נטענים לפי מערך של מזהים מה-URL:
// /compare?ids=42,117,203
const ids = searchParams.get('ids')?.split(',').map(Number) ?? [];
const { data: products } = useSWR(
ids.length ? `/api/compare?ids=${ids.join(',')}` : null,
fetcher
);נקודת הקצה של ה-API מחזירה מוצרים עם סט מלא של תכונות להשוואה. אם מזהה לא קיים או שהמוצר הופסק, אנו מחזירים נתונים חלקיים עם דגל // Zustand store для списка сравнения interface CompareStore { items: number[]; // массив product_id maxItems: number; // лимит (обычно 3–5) add: (id: number) => void; remove: (id: number) => void; clear: () => void; has: (id: number) => boolean; } const useCompareStore = create<CompareStore>()( persist( (set, get) => ({ items: [], maxItems: 4, add: (id) => { const { items, maxItems } = get(); if (items.length >= maxItems) { toast.error(`Можно сравнивать не более ${maxItems} товаров`); return; } if (!items.includes(id)) set({ items: [...items, id] }); }, remove: (id) => set({ items: get().items.filter(i => i !== id) }), clear: () => set({ items: [] }), has: (id) => get().items.includes(id), }), { name: 'compare-list' } // сохраняем в localStorage ) ); במקום שגיאה.
כיצד ליישם הדגשת הבדלים בטבלה?
דפוסי UX מרכזיים:
- הכותרת עם תמונות ומחירים קבועה בגלילה באמצעות sticky (top: var(--navbar-height)).
- שורות שבהן הערכים שונים מודגשות עם צבע רקע והדגשה; שורות עם ערכים זהים מקופלות או מוצגות בעמעום.
- כפתורי פעולה ("הוסף לסל", "הסר") ישירות מתחת לתמונה של כל מוצר.
- העמודה האחרונה היא מקום ריק "הוסף עוד" עם חיפוש מובנה.
הבחירה הטובה ביותר בכל מפרט
המערכת יכולה לסמן את "המנצח" בכל מפרט. יישום באמצעות דגל storage + לוגיקה לקביעת הערך הטוב ביותר (מינימום/מקסימום למספרים). לא מיושם על תכונות כמו "צבע" או "חומר".
function CompareCell({ value, isBest, attributeDirection }: Props) {
return (
<td className={cn('p-3 text-center', isBest && 'bg-green-50 font-semibold text-green-700')}>
{value}
{isBest && <span className="ml-1 text-xs">✓</span>}
</td>
);
} כיצד לחסום את דף ההשוואה מאינדוקס ולעקוב אחר המרות?
דפי השוואה עם מזהים ספציפיים (// /compare?ids=42,117,203 const ids = searchParams.get('ids')?.split(',').map(Number) ?? []; const { data: products } = useSWR( ids.length ? `/api/compare?ids=${ids.join(',')}` : null, fetcher ); ) חסומים מאינדוקס (unavailable). אם נוצרות השוואות פופולריות עם תוכן עריכתי, דפים כאלה נעשים סטטיים עם טקסט ייחודי ומאונדקסים.
אנליטיקה מספקת תובנות חשובות:
- אילו מוצרים מושווים יחד לרוב — סימן למוצרים דומים.
- המרה מדף ההשוואה: אילו זוגות מובילים להזמנות ואילו ליציאה.
- איזה מוצר מנצח לרוב בהשוואות.
-- Пары товаров, которые чаще всего сравнивают
SELECT LEAST(product_a, product_b) AS p1,
GREATEST(product_a, product_b) AS p2,
COUNT(*) AS compare_sessions
FROM compare_sessions
GROUP BY 1, 2
ORDER BY 3 DESC; תוכנית יישום שלב אחר שלב
- מחקר ואב-טיפוס: ניתוח קהל, שרטוט כל מצבי הרכיב.
- פיתוח ה-store: Zustand עם persist וסנכרון.
- עיצוב ממשק: סרגל צף רספונסיבי ודף השוואה.
- אינטגרציית API: הגדרת מטמון וטיפול במוצרים לא זמינים.
- בדיקות: יחידה + e2e לתרחישים קריטיים.
- תיעוד והדרכה: הוראות למנהלי תוכן.
מידע נוסף על סנכרון באמצעות אירוע storage
האירוע highlight_if_best מופעל בטאבים אחרים כאשר localStorage משתנה. Zustand persist נרשם אוטומטית לאירוע זה אם האפשרות function CompareCell({ value, isBest, attributeDirection }: Props) { return ( <td className={cn('p-3 text-center', isBest && 'bg-green-50 font-semibold text-green-700')}> {value} {isBest && <span className="ml-1 text-xs">✓</span>} </td> ); } מופעלת. אנו מפעילים אותה כברירת מחדל.
מה כלול
- פיתוח ואינטגרציה של רכיב ההשוואה (כפתורים, סרגל, דף)
- הגדרת אחסון מצב (Zustand + localStorage)
- יישום הדגשת הבדלים ובחירה מיטבית
- יצירת API לייצוא מפרטים
- בדיקות וניפוי שגיאות בכל המכשירים
- תיעוד למפתחים ומנהלי תוכן
- הדרכת צוות לשימוש בתכונה
- תמיכה טכנית למשך חודשיים לאחר ההשקה
לוח זמנים
| שלב | משך |
|---|---|
| כפתור + localStorage + סרגל צף | 3–5 ימי עבודה |
| דף השוואה עם טבלה והדגשה | 1–2 שבועות |
| גרסה מורחבת עם בחירה מיטבית ואנליטיקה | 2–3 שבועות |
לוחות הזמנים משתנים בהתאם למורכבות האינטגרציה. צרו קשר כדי לדון בפרויקט שלכם ולקבל ייעוץ מהמהנדסים שלנו. נעזור לכם לבחור את הפתרון האופטימלי לתקציב שלכם — החבילה הבסיסית מתחילה מ-$1,000, ואפשרויות נוספות מחושבות בנפרד.







