# XShop – Performance ## 1. Budgets - LCP < 2.5s on mid-tier mobile (4G, Moto G4 class). - Total blocking JS < 150ms. - No render-blocking webfont swap beyond FOIT 100ms (use `font-display: swap`). - DB queries: shop archive < 12 queries, product < 15 (WC baseline excluded). ## 2. Assets - **Conditional enqueue**: each module (`search`, `filter`, `gallery`, `wishlist`) enqueued only where needed (`is_shop`, `is_product`, `is_search`, or presence of shortcode/block). - **No global JS**: single `xshop.js` is ~5KB bootstrap; feature modules lazy-imported via `import()`. - **CSS**: tokens + base always; component CSS split but concatenated in release (critical inline optional, not required v1). - **No jQuery** for new code; WC jQuery remains for its own handlers. - **Images**: `loading="lazy"` + `decoding="async"` + `srcset`, hero eager. Thumbnails via `wp_get_attachment_image`. - **Fonts**: system stack default; optional Vazirmatn self-hosted, subset, no external request by default. ## 3. Queries - Avoid N+1: prime post caches (`update_post_meta_cache`, `update_post_term_cache`), use `wc_get_products` with `return => ids` where only IDs needed, then `wc_get_product` in loop. - Cache expensive counts: filter counts via transient (5 min), search suggestions via transient (5 min). - No queries on `init` for frontend; defer to template. ## 4. Caching - Transients for search/filter counts; `wp_cache` for in-request repeated lookups (e.g., `xshop_get_setting`). - Compatibility with WP Rocket / LiteSpeed – no `DONOTCACHEPAGE` abuse. ## 5. Minification - Release build minifies CSS/JS (`tools/build-release.ps1` runs cssnano/terser or PHP minify). Source maps for dev. ## 6. DOM - Minimal wrappers, no deep nesting. No layout thrash (batch DOM reads/writes). - Carousel: CSS scroll-snap, no heavy JS. ## 7. Monitoring - `System Status` reports: PHP/WP/WC versions, active plugins, asset sizes, transient hit rate. - Lighthouse CI optional (not required for v1). ## 8. Search Performance (M3) - **Debounce**: 250ms in `assets/js/search.js:debounce` (200–300ms target). Prevents request on every keystroke. - **Min length**: 2 chars (handler returns empty without query). Input `data-min-length`. - **Bounded**: `limit` default 6, max 20, validated; handler over-fetches `limit*2` for dedup then slices to `limit`; max 40 IDs scanned. - **Stale protection**: `AbortController` cancels previous fetch + monotonic `seq` counter ignores stale JSON; rapid `iph → iphone` cannot overwrite newer. - **Dedupe**: identical query (`lastQuery`) reopens dropdown without fetch. - **Query**: `WP_Query` `s` + `_sku` LIKE, `post_status publish`, `no_found_rows`, `fields ids`, no N+1, `wc_get_product` only for returned IDs (≤6). - **Asset**: `search.js` enqueued only on front-end where `data-xshop-search` exists (header on all front-end pages, guarded via `initAll`); `woo.css` global intentional. - **Caching**: server `Cache-Control: public, max-age=60`; no client cache beyond in-memory `lastQuery` (no stale invalidation issues). - **Payload**: minimal 6 items × 6 fields (~2KB JSON). ## 9. Anti-Patterns Forbidden - Global `wp_enqueue_script` without condition. - Querying all products to compute filters. - Unbounded `WP_Query` without `posts_per_page` / `no_found_rows`. - External fonts/CDNs by default.