פאג'ינציה באתר: offset, cursor, SEO ו-prefetch

כשקטלוג גדל לעשרות אלפי פריטים ועמודי המוצר מאטים, משתמשים עוזבים ומנועי חיפוש מפספסים תוכן. אנחנו בונים פאג'ינציה ששומרת על מיקום המבקר, מזרזת טעינה ומותאמת לאינדוקס נכון. הצוות שלנו מספק פרויקטים סוהר, מבחירת אלגוריתם ועד הגדרת SEO ותמיכה מתמשכת.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
פאג'ינציה באתר: offset, cursor, SEO ו-prefetch
פשוט
מ- 1 יום עד 3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

פagination של אתר: offset, cursor, SEO ו-prefetch

הערה: כאשר קטלוג עולה על 10,000 פריטים או טבלת לוגים מכילה 500,000 שורות, pagination הוא חיוני. בפרקטיקה שלנו, היה פרויקט עם 1.5 מיליון רשומות ב-PostgreSQL — COUNT בוצע ב-4 שניות גם לאחר אינדוקס. משתמשים איבדו את המיקום שלהם בלחיצה על 'Back', ובוטים של חיפוש לא אינדקסו את כל הדפים. יישמנו pagination עבור יותר מ-50 פרויקטים, מחנויות e-commerce ועד דשבורדים אנליטיים. זה קריטי במיוחד עבור פרויקטים ב-Laravel ו-Next.js, שבהם pagination שגוי מוביל לפגיעה ב-Core Web Vitals וירידה בהמרות. במאמר זה, נפרק את היישום של pagination: מבחירת אלגוריתם ועד תגי SEO ו-prefetch.

בחירת סוג pagination לחנות e-commerce

הבחירה בין offset ל-cursor תלויה במשימה. Offset pagination הוא פשוט: LIMIT 20 OFFSET 40. אבל על מערכי נתונים גדולים, COUNT הוא צוואר בקבוק. אנו משתמשים בספירה מקורבת באמצעות pg_class.reltuples ב-PostgreSQL, ומאיצים את השאילתה פי 100. Cursor pagination מהיר יותר אך אינו מספק URL לכל דף — בעיה עבור SEO.

קריטריון Offset Cursor
מהירות על 1M שורות 2-4 שניות (COUNT) <10 אלפיות השנייה
יציבות תחת הוספות הזזה לא
תאימות SEO כן לא
מורכבות יישום נמוכה בינונית

למה prefetch חשוב למכשירים ניידים?

Prefetch מאפשר טעינת הדף הבא לפני לחיצה. במכשירים ניידים עם חיבורים איטיים, זה מפחית את INP ב-40%. אנו משתמשים ב-react-query עם staleTime: 30_000 וטוענים נתונים מראש ב-useEffect. דוגמה עבור Next.js:

useEffect(() => {
  if (page < lastPage) {
    queryClient.prefetchQuery({
      queryKey: ['products', page + 1],
      queryFn: () => fetchProducts(page + 1),
      staleTime: 30_000,
    });
  }
}, [page]);

השוואת ביצועים: prefetch לעומת ללא prefetch

תרחיש LCP INP זמן מעבר
ללא prefetch 2.8 שניות 200 אלפיות השנייה 600 אלפיות השנייה
עם prefetch 2.1 שניות 120 אלפיות השנייה 100 אלפיות השנייה

מתי להשתמש ב-cursor pagination?

Cursor pagination מתאים לפידים בזמן אמת (Twitter, פיד תגובות) או ל-APIs עם נפחי נתונים גדולים שבהם סדר ללא הזזות חשוב. אנו משתמשים בו בפרויקטים עם יותר מ-100,000 רשומות עם הוספות תכופות. Cursor pagination דורש מפתח מיון ייחודי (למשל, ID או created_at) ואינו תומך בקפיצות שרירותיות בין דפים, אך מבטיח יציבות כאשר מתווספות רשומות חדשות.

מדריך שלב-אחר-שלב ליישום pagination ב-Next.js

  1. הגדר את חוזה ה-API: בחר offset או cursor. עבור offset, החזר useEffect(() => { if (page < lastPage) { queryClient.prefetchQuery({ queryKey: ['products', page + 1], queryFn: () => fetchProducts(page + 1), staleTime: 30_000, }); } }, [page]); , current_page, last_page, total.
  2. יישם לוגיקת שרת: השתמש ב-per_page ב-Laravel או ב-paginate() ב-MongoDB. שמור COUNT במטמון באמצעות Redis למשך 5 דקות.
  3. פתח את רכיב ה-UI: עם אליפסיס, aria-label, ורספונסיביות.
  4. סנכרן עם ה-URL: באמצעות find().skip().limit() ב-Next.js או useSearchParams ב-JavaScript ונילה.
  5. הוסף prefetch: עם React Query או history.pushState עם useEffect.
  6. יישם תגי SEO: fetch ו-<link rel="prev"/> עבור Yandex. Google אינו משתמש ב-rel prev/next לדירוג (Google Search Central), אך הם חשובים עבור Yandex.
  7. בדוק: ודא ש-LCP < 2.5 שניות, INP < 200 אלפיות השנייה.

צד שרת: offset לעומת cursor pagination

כדי להאיץ את COUNT, אנו משתמשים בספירה מקורבת באמצעות <link rel="next"/> ב-PostgreSQL או במטמון ב-Redis למשך 5 דקות. זה מפחית את זמן השאילתה משניות לאלפיות השנייה.

// Laravel — стандартный paginate()
public function index(Request $request): JsonResponse
{
    $perPage = min($request->integer('per_page', 20), 100);
    $products = Product::where('is_active', true)
        ->orderByDesc('created_at')
        ->paginate($perPage);

    return response()->json([
        'data' => ProductResource::collection($products->items()),
        'current_page' => $products->currentPage(),
        'last_page' => $products->lastPage(),
        'per_page' => $products->perPage(),
        'total' => $products->total(),
        'from' => $products->firstItem(),
        'to' => $products->lastItem(),
    ]);
}

רכיב UI ל-pagination

הרכיב מציג מספרי עמודים עם אליפסיס עבור עמודים רבים. לכפתורים יש pg_class.reltuples לנגישות. העמוד הפעיל מודגש. מעוצב עם Tailwind.

// components/Pagination.tsx
interface PaginationProps {
  currentPage: number
  lastPage: number
  onPageChange: (page: number) => void
}

export function Pagination({ currentPage, lastPage, onPageChange }: PaginationProps) {
  const pages = buildPageRange(currentPage, lastPage)
  return (
    <nav aria-label="Навигация по страницам">
      <ul className="flex items-center gap-1">
        <li>
          <button
            onClick={() => onPageChange(currentPage - 1)}
            disabled={currentPage === 1}
            aria-label="Предыдущая страница"
          >
            ←
          </button>
        </li>
        {pages.map((page, i) =>
          page === '...' ? (
            <li key={`ellipsis-${i}`} aria-hidden="true">…</li>
          ) : (
            <li key={page}>
              <button
                onClick={() => onPageChange(page as number)}
                aria-current={page === currentPage ? 'page' : undefined}
                className={page === currentPage ? 'font-bold' : ''}
              >
                {page}
              </button>
            </li>
          )
        )}
        <li>
          <button
            onClick={() => onPageChange(currentPage + 1)}
            disabled={currentPage === lastPage}
            aria-label="Следующая страница"
          >
            →
          </button>
        </li>
      </ul>
    </nav>
  )
}

function buildPageRange(current: number, last: number): (number | '...')[] {
  if (last <= 7) return Array.from({ length: last }, (_, i) => i + 1)
  const delta = 2
  const range: (number | '...')[] = []
  const left = current - delta
  const right = current + delta
  let prev: number | null = null
  for (let i = 1; i <= last; i++) {
    if (i === 1 || i === last || (i >= left && i <= right)) {
      if (prev !== null && i - prev > 1) range.push('...')
      range.push(i)
      prev = i
    }
  }
  return range
}

סנכרון URL

מספר העמוד חייב להיות ב-URL — אחרת המשתמש מאבד את המיקום ברענון ואינו יכול לשתף את הקישור. אנו משתמשים ב-// Laravel — стандартный paginate() public function index(Request $request): JsonResponse { $perPage = min($request->integer('per_page', 20), 100); $products = Product::where('is_active', true) ->orderByDesc('created_at') ->paginate($perPage); return response()->json([ 'data' => ProductResource::collection($products->items()), 'current_page' => $products->currentPage(), 'last_page' => $products->lastPage(), 'per_page' => $products->perPage(), 'total' => $products->total(), 'from' => $products->firstItem(), 'to' => $products->lastItem(), ]); } ב-Next.js App Router.

// Next.js App Router 'use client'
import { useRouter, useSearchParams, usePathname } from 'next/navigation'

function usePageParam() {
  const searchParams = useSearchParams()
  const router = useRouter()
  const pathname = usePathname()

  const page = Number(searchParams.get('page') ?? '1')

  const setPage = (newPage: number) => {
    const params = new URLSearchParams(searchParams.toString())
    params.set('page', String(newPage))
    router.push(`${pathname}?${params.toString()}`, { scroll: true })
  }

  return [page, setPage] as const
}

מה כלול בעבודת ה-pagination שלנו

  • ביקורת על תוכנית ה-pagination הנוכחית (שאילתות SQL, זמן תגובה)
  • עיצוב חוזה API (offset/cursor, פורמט תגובה)
  • יישום לוגיקת שרת עם מטמון COUNT
  • פיתוח רכיב UI עם רספונסיביות ונגישות
  • סנכרון URL (Next.js, React Router, Vanilla)
  • הגדרת prefetch למעברים מיידיים
  • שילוב תגי SEO (rel prev/next, canonical, sitemap עבור pagination)
  • תיעוד והדרכה לצוות הלקוח

לוחות זמנים ועלות

רכיב בסיסי עם אינטגרציית API — מיום אחד. חבילה מלאה עם סנכרון URL, prefetch, תגי SEO, עיצוב רספונסיבי והדרכה — מיומיים עד 4 ימים. העלות מחושבת באופן אישי לאחר ניתוח הפרויקט שלך. חיסכון בזמן פיתוח על רכיבים מוכנים הוא עד 30% בהשוואה לכתיבה מאפס.

צור קשר להערכה חינמית של הפרויקט שלך. המהנדסים שלנו עם שנים של ניסיון יעזרו ליישם pagination תוך התחשבות ב-SEO וביצועים. אנו מבטיחים LCP < 2.5 שניות ואינדוקס נכון ב-Yandex. הזמן יישום pagination עם אחריות ל-Core Web Vitals. קבל ייעוץ מהנדס ואב-טיפוס ביום אחד.