معماری JavaScript
اصل کلی
یک کامپوننت ریشهی مشترک روی <body> همهی صفحات، بهعلاوهی یک ماژول اختیاری برای صفحاتی که تعامل بیشتری دارند. همهچیز تدریجی است: صفحهای که سبد ندارد، هرگز متدهای سبد را صدا نمیزند.
<body class="sp-page sp-theme--fresh" x-data="superium()" x-init="init()">
superium() - رفتار مشترک
در assets/js/superium.js. چیزی که نگه میدارد:
| گروه | اعضا |
|---|---|
| رابط کاربری | mobileMenuOpen، searchOpen، megaOpen، megaCat، cartOpen، userOpen، supportOpen |
| داده | products، categories |
| سبد | cart، addToCart(id, qty)، decrement(id)، removeFromCart(id)، clearCart()، qtyOf(id)، cartLine(id) |
| علاقهمندی | favourites، isFavourite(id)، toggleFavourite(id) |
| کمکی | fa(n) ارقام فارسی، toman(n) قیمت، product(id)، byCategory(slug, limit)، notify(msg) |
نمونهی استفاده در همان HTML:
<button @click="addToCart('dairy-1234567')">افزودن به سبد</button>
<span x-text="fa(qtyOf('dairy-1234567'))"></span>
ماندگاری داده
کلید localStorage | محتوا |
|---|---|
superium.cart | اقلام سبد [{ id, qty }] |
superium.favourites | فهرست شناسهی کالاهای علاقهمندی |
به همین دلیل سبد بین صفحات استاتیک باقی میماند. برای وصلکردن به بکاند واقعی، persistCart() را با فراخوانی API خودتان جایگزین کنید - بقیهی رابط کاربری دستنخورده میماند.
دادهی کالاها
<script src="assets/data/products.js"></script>
این فایل window.SUPERIUM_PRODUCTS را مقدار میدهد:
window.SUPERIUM_PRODUCTS = {
categories: [{ slug, title, icon }, …],
products: [{ id, title, category, price, final, image, thumb, … }, …]
};
عمداً یک
<script>معمولی است و نهfetch، تا قالب باfile://هم کار کند. اگر بکاند دارید، همینجا را با یکfetchبه API خودتان عوض کنید.
ماژولهای هر صفحه
| فایل | کامپوننت | صفحه |
|---|---|---|
superium-market.js | superiumMarket() | فروشگاه - فیلتر، مرتبسازی، صفحهبندی |
superium-product.js | superiumProduct() | جزئیات کالا - گالری |
superium-checkout.js | superiumCheckout() | تسویه - پیک/حضوری، کد تخفیف، صورتحساب |
superium-auth.js | superiumAuth() | ورود و ثبتنام - کد یکبارمصرف |
superium-map.js | - | نقشه؛ به [data-sp-map] وصل میشود |
superium-demo.js | - | لایتباکس demo.html |
هر کدام روی یک عنصر داخل صفحه سوار میشوند:
<div class="sp-container" x-data="superiumMarket()" x-init="initMarket($el)">
نمونه: فیلترهای فروشگاه
superiumMarket() آدرس را میخواند و فیلترها را از آن میسازد:
var params = new URLSearchParams(window.location.search);
var cat = params.get('c'); // دستهبندی
var q = params.get('q'); // جستجو در عنوان
var sort = params.get('sort'); // مرتبسازی
خروجی از یک getter به نام visible میآید که ترتیب فیلترها را اعمال میکند و بعد مرتب میشود؛ paged همان را برای صفحهی جاری برش میزند.
برای افزودن یک فیلتر تازه:
- یک ویژگی به state اضافه کنید (مثلاً
onlyFreeShipping: false). - یک شرط داخل getter
visibleبگذارید. - در HTML یک
x-modelبه آن وصل کنید. - در
resetFilters()مقدارش را برگردانید.
ریلها و اسلایدرها
Swiper در superium.js راه میافتد و هر عنصری با این نشانهها را میگیرد:
<section data-sp-rail-scope>
<button data-sp-rail-prev>…</button>
<button data-sp-rail-next>…</button>
<div class="swiper" data-sp-rail>
<div class="swiper-wrapper"> … </div>
</div>
</section>
ترتیب اسکریپتها
products.js → swiper-bundle.min.js → superium.js → superium-<صفحه>.js → alpine.js
alpine.jsباید آخر باشد. Alpine در لحظهی بارگذاری DOM را میخواند؛ اگر زودتر بیاید،superium()هنوز تعریف نشده و کنسول خطایsuperium is not definedمیدهد.
اتصال به بکاند
سه نقطهی تماس کافی است:
- کالاها:
assets/data/products.jsرا با پاسخ API خودتان جایگزین کنید (همان شکل داده). - سبد:
persistCart()درsuperium.js. - فرمها: فرمهای ورود، ثبتنام، آدرس و تسویه فعلاً
@submit.preventدارند؛ بهجایش درخواست خودتان را بفرستید.