רכיב השוואת מוצרים ראקטיבי על 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 מוצרים מעמיסים על הממשק — עדיף להגביל.
- יצרת טבלה ללא עמודה מקובעת במובייל. המשתמש אינו רואה שמות מאפיינים בעת גלילה אופקית.
כל הבעיות הללו נפתרות בשלב הפיתוח — צור קשר לייעוץ.
סיכום
השוואת מוצרים היא כלי חשוב עבור לקוחות הבוחרים בין פריטים דומים. השוואה מיושמת כראוי עוזרת לקבל החלטה באתר במקום לעזוב לאגרגטור. הרכיב שלנו מהיר, מודרני ואדפטיבי. רוצה לשפר את חוויית המשתמש בחנות שלך? צור קשר — נדון בפרטים ונכין הצעה מסחרית.

