רכיב השוואת מוצרים ראקטיבי על Vue.js עבור Bitrix
תרחיש טיפוסי: משתמש מוסיף מוצר להשוואה, הדף נטען מחדש, המונה משתנה. אחרי שלוש לחיצות, הם עוזבים למרקטפלייס. רכיבי ההשוואה הסטנדרטיים של 1C-Bitrix (bitrix:catalog.compare.buy ו-bitrix:catalog.compare.result) עבדו היטב לפני שנים, אבל היום הציפיות לחוויית משתמש השתנו באופן קיצוני. אנו מציעים להחליף אותו בפתרון מודרני מבוסס Vue.js: הוספה להשוואה היא מיידית, המונה בכותרת מתעדכן ללא טעינה מחדש, והטבלה יכולה להסתיר מאפיינים זהים. אנו מפתחים את הרכיב במפתח מלא, עם עיצוב אדפטיבי ותמיכה.
למה להחליף את ההשוואה הסטנדרטית ב-Vue.js?
הרכיב הסטנדרטי משתמש ב-PHP session כדי לאחסן את הרשימה — כלומר טעינה מחדש בכל פעולה. גרסת ה-Vue.js עובדת כולה בצד הלקוח: המצב נשמר ב-Pinia store, מסונכרן עם localStorage עבור אורחים ועם השרת עבור משתמשים מחוברים. זה נותן מהירות, תגובתיות, ושמירה בין סשנים. כתוצאה מכך, זמן הוספת מוצר יורד מ-1–2 שניות ל-50 אלפיות השנייה, והמרת העגלה עולה ב-15–25%.
איך הרכיב עובד
שמירת רשימת ההשוואה
Bitrix סטנדרטי שומר השוואה ב-PHP session. אנו משתמשים ב-localStorage עם סנכרון שרת עבור משתמשים מחוברים — כך שהרשימה נשמרת בין סשנים ומכשירים.
// stores/compareStore.ts (Pinia)
export const useCompareStore = defineStore('compare', {
state: () => ({
items: [] as number[], // product IDs
}),
actions: {
async add(productId: number) {
if (this.items.includes(productId)) return
if (this.items.length >= 4) {
alert('Можно сравнивать до 4 товаров одновременно')
return
}
this.items.push(productId)
localStorage.setItem('compare_items', JSON.stringify(this.items))
// Синхронизация с сервером для авторизованных
if (isLoggedIn()) {
await fetch('/local/api/compare/add/', {
method: 'POST',
body: JSON.stringify({ product_id: productId }),
})
}
},
remove(productId: number) {
this.items = this.items.filter(id => id !== productId)
localStorage.setItem('compare_items', JSON.stringify(this.items))
},
isInCompare: (state) => (productId: number) => state.items.includes(productId),
},
getters: {
count: (state) => state.items.length,
},
})בעת אתחול האפליקציה, הרשימה נטענת מ-localStorage (לאורחים) או מה-API (למשתמשים מחוברים, עם מיזוג בין מכשירים).
כפתור "השווה" בכרטיס המוצר
<!-- CompareButton.vue -->
<template>
<button
:class="['compare-btn', { 'compare-btn--active': isAdded }]"
@click="toggle"
:title="isAdded ? 'Убрать из сравнения' : 'Добавить к сравнению'"
>
<IconScale :filled="isAdded" />
<span>{{ isAdded ? 'В сравнении' : 'Сравнить' }}</span>
</button>
</template>
<script setup lang="ts">
const props = defineProps<{ productId: number }>()
const store = useCompareStore()
const isAdded = computed(() => store.isInCompare(props.productId))
function toggle() {
isAdded.value ? store.remove(props.productId) : store.add(props.productId)
}
</script>הכפתור מותקן על כל כרטיס מוצר דרך data-attribute.
מונה בכותרת
<!-- CompareCounter.vue (монтируется в header) -->
<template>
<a href="/compare/" class="header-compare">
<IconScale />
<span class="badge" v-if="count > 0">{{ count }}</span>
</a>
</template>
<script setup lang="ts">
const store = useCompareStore()
const count = computed(() => store.count)
</script>המונה מתעדכן ראקטיבית דרך Pinia — ללא EventBus גלובלי.
טבלת השוואה עם מסנן הבדלים
החלק הקשה ביותר הוא הטבלה. הנתונים נטענים מהשרת לפי רשימת המזהים, ולאחר מכן נבנית מפת הבדלים בצד הלקוח. הכפתור "הצג רק הבדלים" מסתיר שורות עם ערכים זהים.
<!-- CompareTable.vue (упрощённо) -->
<template>
<table class="compare-table">
<thead>
<tr>
<th>Характеристика</th>
<th v-for="p in products" :key="p.id">
<ProductCard :product="p" @remove="store.remove(p.id)" />
</th>
</tr>
</thead>
<tbody>
<tr v-for="spec in visibleSpecs" :key="spec" :class="{ 'row--different': diffMap[spec] }" >
<td class="spec-name">{{ specLabels[spec] }}</td>
<td v-for="p in products" :key="p.id">{{ p.specs[spec] ?? '—' }}</td>
</tr>
</tbody>
</table>
</template>
<script setup lang="ts">
const showOnlyDiff = ref(false)
const visibleSpecs = computed(() => showOnlyDiff.value ? Object.keys(diffMap.value).filter(k => diffMap.value[k]) : Object.keys(diffMap.value) )
</script>במובייל, העמודה הראשונה מקובעת באמצעות CSS Grid עם // stores/compareStore.ts (Pinia) export const useCompareStore = defineStore('compare', { state: () => ({ items: [] as number[], // product IDs }), actions: { async add(productId: number) { if (this.items.includes(productId)) return if (this.items.length >= 4) { alert('Можно сравнивать до 4 товаров одновременно') return } this.items.push(productId) localStorage.setItem('compare_items', JSON.stringify(this.items)) // Синхронизация с сервером для авторизованных if (isLoggedIn()) { await fetch('/local/api/compare/add/', { method: 'POST', body: JSON.stringify({ product_id: productId }), }) } }, remove(productId: number) { this.items = this.items.filter(id => id !== productId) localStorage.setItem('compare_items', JSON.stringify(this.items)) }, isInCompare: (state) => (productId: number) => state.items.includes(productId), }, getters: { count: (state) => state.items.length, }, }) .
איך לשלב את הרכיב בתבנית?
השילוב כולל שלושה שלבים:
- הצב נקודות עיגון בתבניות PHP:
<!-- CompareButton.vue --> <template> <button :class="['compare-btn', { 'compare-btn--active': isAdded }]" @click="toggle" :title="isAdded ? 'Убрать из сравнения' : 'Добавить к сравнению'" > <IconScale :filled="isAdded" /> <span>{{ isAdded ? 'В сравнении' : 'Сравнить' }}</span> </button> </template> <script setup lang="ts"> const props = defineProps<{ productId: number }>() const store = useCompareStore() const isAdded = computed(() => store.isInCompare(props.productId)) function toggle() { isAdded.value ? store.remove(props.productId) : store.add(props.productId) } </script>לכפתורים בכרטיסים,<!-- CompareCounter.vue (монтируется в header) --> <template> <a href="/compare/" class="header-compare"> <IconScale /> <span class="badge" v-if="count > 0">{{ count }}</span> </a> </template> <script setup lang="ts"> const store = useCompareStore() const count = computed(() => store.count) </script>למונה בכותרת,<!-- CompareTable.vue (упрощённо) --> <template> <table class="compare-table"> <thead> <tr> <th>Характеристика</th> <th v-for="p in products" :key="p.id"> <ProductCard :product="p" @remove="store.remove(p.id)" /> </th> </tr> </thead> <tbody> <tr v-for="spec in visibleSpecs" :key="spec" :class="{ 'row--different': diffMap[spec] }" > <td class="spec-name">{{ specLabels[spec] }}</td> <td v-for="p in products" :key="p.id">{{ p.specs[spec] ?? '—' }}</td> </tr> </tbody> </table> </template> <script setup lang="ts"> const showOnlyDiff = ref(false) const visibleSpecs = computed(() => showOnlyDiff.value ? Object.keys(diffMap.value).filter(k => diffMap.value[k]) : Object.keys(diffMap.value) ) </script>לדף ההשוואה. - כלול את ה-bundle המובנה של Vue.js דרך
position: stickyבאפילוג התבנית. - הגדר בקר API כדי להביא נתוני מוצר לפי רשימת מזהים (מאפיינים עם
data-compare-btn).
מקרה בוחן: עלייה של 20% בהמרה לאחר היישום
אחד הלקוחות שלנו — חנות מקוונת עם קטלוג של כ-5,000 מוצרים — התמודד עם המרה נמוכה בדף ההשוואה (רק 2% מעבר לעגלה). הרכיב הסטנדרטי היה איטי: כל פעולה גרמה לטעינה מחדש. לאחר יישום רכיב ה-Vue.js, מהירות הבחירה ירדה ב-70%, וחלקם של המשתמשים שהוסיפו מוצר לעגלה מדף ההשוואה גדל ל-22%. התכונה המרכזית הייתה הכפתור "הצג רק הבדלים" — לקוחות מצאו הבדלים מהר יותר וקיבלו החלטה.
השוואת גישות: סטנדרטי לעומת Vue.js
| פרמטר | רכיב סטנדרטי | רכיב Vue.js |
|---|---|---|
| זמן הוספת מוצר | ~1-2 שניות (עם טעינה מחדש) | ~50 אלפיות השנייה (ללא טעינה מחדש) |
| שמירת רשימה | רק PHP session | localStorage + סנכרון שרת |
| עיצוב אדפטיבי למובייל | מוגבל | CSS Grid עם עמודה מקובעת |
| מסנן הבדלים | לא | כן |
| המרת עגלה | ~2% | ~22% (נתוני מקרה) |
מה כלול
אנו מספקים רכיב מוכן לשימוש הכולל:
- קוד מקור ב-Vue.js 3 + Pinia + TypeScript.
- בקר PHP עבור API ההשוואה (אופציונלי).
- הוראות שילוב לתבנית 1C-Bitrix.
- גישה למאגר עם היסטוריית שינויים.
- תמיכה בייעוץ למשך שבועיים לאחר המסירה.
תהליך ולוח זמנים
| שלב | משך | תוצאה |
|---|---|---|
| ניתוח ועיצוב | 1–2 ימים | מפרט טכני, פריסת שילוב |
| פיתוח רכיב | 5–10 ימים | אב טיפוס עובד |
| שילוב ובדיקות | 2–3 ימים | פונקציונליות מלאה בסביבת staging |
| פריסה ותיעוד | יום אחד | מסירת קוד והוראות |
לוח זמנים משוער: 1 עד 3 שבועות תלוי במורכבות. צור קשר — נבחן את הפרויקט שלך.
הניסיון שלנו
אנו עוסקים בפיתוח Bitrix למעלה מ-5 שנים והשלמנו יותר מ-50 פרויקטים, כולל רכיבים מותאמים אישית לחנויות מקוונות. אנו מכירים את כל המלכודות בשילוב Vue.js עם 1C-Bitrix ומבטיחים איכות. קבל ייעוץ — נדון בפרטי החנות שלך.
פרטים טכניים (למפתחים)
טכנולוגיות: Vue.js 3, Pinia, TypeScript, Vite. בנייה דרך Webpack או Vite, מצורפת כנקודת כניסה נפרדת בתבנית. הרכיב אינו מתנגש עם אפליקציות Vue אחרות בדף.
מאפייני בלוק מידע המשתתפים בהשוואה מסומנים בדגל data-compare-counter — זהו מנגנון Bitrix סטנדרטי שאינו דורש קוד נוסף.
טעויות נפוצות והפתרונות שלהן
- שכחו סנכרון למשתמשים מחוברים. בלעדיו, הרשימה אובדת כשמנקים את מטמון הדפדפן.
- לא התחשבו במגבלת המוצרים. יותר מ-4–5 מוצרים מעמיסים על הממשק — עדיף להגביל.
- יצרו טבלה ללא עמודה מקובעת במובייל. המשתמש לא רואה שמות מאפיינים בגלילה אופקית.
כל הבעיות הללו נפתרות בשלב הפיתוח — צור קשר לייעוץ.
סיכום
השוואת מוצרים היא כלי חשוב ללקוחות הבוחרים בין פריטים דומים. השוואה מיושמת כראוי עוזרת לקבל החלטה באתר במקום לעזוב לאגרגטור. הרכיב שלנו מהיר, מודרני ואדפטיבי. רוצה לשפר את חוויית המשתמש בחנות שלך? צור קשר — נדון בפרטים ונכין הצעה מסחרית.

