صفحه‌بندی در توسعه موبایل — چیست، انواع و نحوه کار

نویسنده: IT Sectr منتشر شده: 2026-03-11 زمان مطالعه: 9 دقیقه

صفحه‌بندی — تکنیک بارگذاری داده‌ها به صورت صفحه‌به‌صفحه است که در اپلیکیشن‌های موبایل و سرویس‌های وب برای کار با مجموعه‌های بزرگ رکوردها استفاده می‌شود. طبق Android Developers Documentation (2025)، پیاده‌سازی صحیح صفحه‌بندی بار API را کاهش می‌دهد، در ترافیک صرفه‌جویی می‌کند و تجربه کاربری را بهبود می‌بخشد. بارگذاری صفحه‌ای به اپلیکیشن اجازه می‌دهد محتوا را به تدریج و بدون انتظار برای بارگذاری کامل تمام داده‌ها نمایش دهد.

نکات اصلی

  • صفحه‌بندی — روش بارگذاری مجموعه‌های بزرگ داده به صورت تکه‌تکه برای بهینه‌سازی عملکرد و ترافیک.
  • صفحه‌بندی Offset از جابجایی (page/offset) برای ناوبری استفاده می‌کند — روشی ساده اما ناپایدار در درج‌های مکرر.
  • صفحه‌بندی Cursor از نشانگر یکتای آخرین رکورد استفاده می‌کند — در تغییر داده بین درخواست‌ها پایدار است.
  • صفحه‌بندی Keyset بر اساس ستون با ایندکس یکتا فیلتر می‌کند — برای جداول بزرگ بدون تکراری کارآمد است.
  • صفحه‌بندی Time-based رکوردها را بر اساس برچسب‌های زمانی گروه‌بندی می‌کند — برای فیدهای خبری و شبکه‌های اجتماعی مناسب است.

صفحه‌بندی چیست؟

صفحه‌بندی (از انگلیسی pagination — تقسیم به صفحات) — تکنیکی برای تقسیم یک مجموعه داده بزرگ به بخش‌های متوالی (صفحات) است. در اپلیکیشن‌های موبایل، صفحه‌بندی هنگام بارگذاری لیست پیام‌ها، فید خبری، کاتالوگ محصولات، تاریخچه سفارشات و هر مجموعه دیگری با تعداد بالقوه نامحدود رکورد استفاده می‌شود.

بدون صفحه‌بندی، اپلیکیشن مجبور به بارگذاری همه داده‌ها به یکباره است که منجر به انتظار طولانی، مصرف بالای ترافیک و عملکرد ناپایدار در دستگاه‌های ضعیف می‌شود. درخواست API با صفحه‌بندی فقط یک تکه داده و متا-اطلاعات برای بارگذاری تکه بعدی را برمی‌گرداند — بنابراین اپلیکیشن حجم اطلاعات دریافتی را کنترل می‌کند.

معیارهای اصلی صفحه‌بندی: اندازه صفحه (page size) — تعداد رکوردها در یک صفحه (معمولاً 10-50)، و شماره صفحه یا نشانگر — اشاره‌گر به موقعیت فعلی در مجموعه. انتخاب اندازه صفحه به نوع داده بستگی دارد: برای عناصر فشرده (نام‌ها) 20-30 کافی است، برای کارت‌های دارای تصویر — 10-15.

چرا صفحه‌بندی در اپلیکیشن‌های موبایل ضروری است

دستگاه‌های موبایل منابع محدودی دارند: حجم حافظه رم، سرعت پردازنده و محدودیت ترافیک. صفحه‌بندی سه وظیفه کلیدی را حل می‌کند: کاهش مصرف حافظه (فقط عناصر قابل مشاهده در حافظه ذخیره می‌شوند)، تسریع نمایش اولیه (اولین تکه سریع‌تر از کل مجموعه بارگذاری می‌شود) و صرفه‌جویی در ترافیک (داده‌ها فقط وقتی کاربر لیست را اسکرول می‌کند بارگذاری می‌شوند).

انواع اصلی صفحه‌بندی

چهار نوع اصلی صفحه‌بندی وجود دارد که هر کدام وظایف خاصی را حل می‌کنند. انتخاب روش به نیازهای سازگاری داده‌ها، معماری API، نوع ذخیره‌سازی و پیچیدگی مجاز پیاده‌سازی در سمت کلاینت و سرور بستگی دارد.

نوعنحوه کارپایداریسرعت در حجم‌های بزرگ
OffsetLIMIT + OFFSET در SQLکمبا افزایش OFFSET کاهش می‌یابد
CursorWHERE id > last_idزیادپایدار (O(log n))
KeysetWHERE key > last_keyزیادپایدار (O(log n))
Time-basedWHERE created_at < last_timeمتوسطپایدار با ایندکس

چه زمانی از کدام نوع استفاده کنیم

صفحه‌بندی Offset برای مجموعه داده‌های ایستا یا به ندرت به‌روزرسانی‌شونده مناسب است، زمانی که پیاده‌سازی ساده اهمیت دارد. Cursor و Keyset — برای داده‌های پویا با درج‌های مکرر. Time-based — برای فیدهای زمانی که رکوردها بر اساس زمان ایجاد مرتب می‌شوند. استاندارد GraphQL Relay از صفحه‌بندی cursor به عنوان تنها روش توصیه‌شده استفاده می‌کند.

صفحه‌بندی Offset: مزایا و معایب

صفحه‌بندی Offset — ساده‌ترین نوع بارگذاری صفحه‌ای است. کلاینت پارامترهای page و limit (یا offset و limit) را ارسال می‌کند، سرور SQL OFFSET و LIMIT را اعمال می‌کند. مثلاً page=2, limit=20 رکوردهای 21 تا 40 را برمی‌گرداند. این روش به طور شهودی قابل درک است و به راحتی در هر پشته‌ای پیاده‌سازی می‌شود.

python
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items")
async def get_items(
    page: int = Query(default=1, ge=1),
    limit: int = Query(default=20, le=100)
):
    offset = (page - 1) * limit
    items = await fetch_items(offset, limit)
    total = await count_items()
    return {
        "items": items,
        "total": total,
        "page": page,
        "pages": (total + limit - 1) // limit
    }

مشکل ناسازگاری داده‌ها

عیب اصلی صفحه‌بندی Offset — مشکل رکوردهای جاافتاده و تکراری. اگر بین دو درخواست رکوردهای جدیدی به جدول اضافه شود، OFFSET جابجا می‌شود: کاربر ممکن است یک رکورد را دو بار ببیند یا رکورد جدید را از دست بدهد. این برای فیدهای خبری و چت‌ها که سازگاری اهمیت دارد بحرانی است.

مشکل دیگر — کاهش عملکرد در OFFSETهای بزرگ. پایگاه داده مجبور است قبل از بازگرداندن نتیجه، اولین offset رکورد را اسکن کرده و رد کند. در offset=100000 حتی با LIMIT 20، سرور زمان قابل توجهی را صرف اسکن می‌کند. PostgreSQL و MySQL کاهش سرعت خطی با افزایش OFFSET نشان می‌دهند.

وقتی Offset هنوز انتخاب خوبی است

صفحه‌بندی Offset همچنان بهترین انتخاب است برای: پنل‌های مدیریتی (داده‌ها به ندرت تغییر می‌کنند، ناوبری بین صفحات لازم است)، گزارش‌ها و لاگ‌های تاریخی (برش ثابتی از داده)، کاتالوگ‌های دارای فیلتر (می‌توان به هر صفحه‌ای رفت). Offset همچنین ساده‌ترین پیاده‌سازی را در سمت کلاینت دارد — RecyclerView با Paging 3 آن را به صورت داخلی پشتیبانی می‌کند.

صفحه‌بندی Keyset و Time-based

صفحه‌بندی Keyset از یک کلید یکتا (معمولاً کلید اصلی) برای فیلتر کردن رکوردها استفاده می‌کند. به جای OFFSET، درخواست از WHERE id > last_seen_id استفاده می‌کند. این امر عملکرد پایدار صرف‌نظر از تعداد رکوردها و عدم وجود تکراری در درج‌ها را تضمین می‌کند، زیرا رکوردهای جدید همیشه id بزرگتری دارند.

sql
-- صفحه‌بندی Offset (مشکل‌دار)
SELECT * FROM posts
ORDER BY id
LIMIT 20 OFFSET 100;

-- صفحه‌بندی Keyset (پایدار)
SELECT * FROM posts
WHERE id > 100
ORDER BY id
LIMIT 20;

صفحه‌بندی Time-based

صفحه‌بندی Time-based (یا نشانگر زمانی) از برچسب زمانی created_at برای ناوبری استفاده می‌کند. کلاینت timestamp آخرین رکورد بارگذاری‌شده را ارسال می‌کند، سرور رکوردهای ایجاد شده قبل یا بعد از این برچسب را برمی‌گرداند. این روش در شبکه‌های اجتماعی و فیدهای خبری محبوب است، جایی که ترتیب رکوردها بر اساس زمان انتشار تعیین می‌شود.

ویژگی صفحه‌بندی Time-based — احتمال تکراری بودن اگر دو رکورد در یک میلی‌ثانیه ایجاد شوند. برای رفع این مشکل، کلید time-based با id یکتا ترکیب می‌شود: WHERE (created_at, id) < (last_time, last_id). چنین نشانگر ترکیبی یکتایی هر رکورد و ترتیب دقیق را تضمین می‌کند.

مقایسه Keyset و Time-based

صفحه‌بندی Keyset به ستونی با مقدار یکتا و به طور یکنواخت افزایش‌یابنده نیاز دارد (id خودافزایش‌شونده، UUID v7). Time-based برای هر جدولی با created_at مناسب است، اما نیاز به پردازش اضافی تکراری‌ها دارد. تفاوت اصلی: Keyset در هر عملیات درج به طور پایدار کار می‌کند، در حالی که Time-based به برچسب‌های زمانی یکسان حساس است.

چگونه نوع صفحه‌بندی را برای پروژه انتخاب کنیم

انتخاب نوع صفحه‌بندی به ماهیت داده‌ها و نیازهای تجربه کاربری بستگی دارد. در زیر توصیه‌هایی برای سناریوهای معمول در توسعه موبایل آورده شده است. راه حل جهانی وجود ندارد — هر روش حوزه کاربردی دارد که در آن بهینه است.

  • چت / پیام‌رسان — صفحه‌بندی Cursor (بر اساس id پیام). پیام‌های جدید از بالا اضافه می‌شوند، نشانگر منحرف نمی‌شود.
  • فید خبری — صفحه‌بندی Time-based (بر اساس created_at). رکوردها بر اساس زمان مرتب می‌شوند، زمان‌شناسی مهم است.
  • کاتالوگ محصولات — صفحه‌بندی Offset. کاربر می‌تواند به صفحه خاصی برود، داده‌ها به ندرت تغییر می‌کنند.
  • تاریخچه سفارشات — صفحه‌بندی Cursor. پایداری مهم است زیرا سفارش‌های جدید بین بارگذاری‌ها اضافه می‌شوند.
  • نظرات — صفحه‌بندی Keyset. هر نظر id یکتا دارد، حجم‌های بزرگ بدون تکراری.

پیاده‌سازی در Android با Paging 3

کتابخانه Android Paging 3 از همه انواع صفحه‌بندی از طریق PagingSource پشتیبانی می‌کند. برای Offset — PagingSource با کلید Int (page)، برای Cursor — با کلید String یا Long (cursor). PagingSource به طور خودکار بارگذاری، کش کردن و تلاش مجدد در خطاها را مدیریت می‌کند.

kotlin
class PostPagingSource(
    private val api: PostApi
) : PagingSource<Long, Post>() {

    override suspend fun load(
        params: LoadParams<Long>
    ): LoadResult<Long, Post> {
        val cursor = params.key ?: Long.MAX_VALUE
        return try {
            val response = api.getPosts(cursor, params.loadSize)
            LoadResult.Page(
                data = response.items,
                prevKey = null,
                nextKey = response.items.lastOrNull()?.id
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Long, Post>): Long? {
        return state.anchorPosition?.let {
            state.closestItemToPosition(it)?.id
        }
    }
}

توصیه‌هایی برای اندازه صفحه

اندازه صفحه بر سرعت بارگذاری و درک عملکرد تأثیر می‌گذارد. برای اپلیکیشن‌های موبایل، محدوده بهینه 10-25 عنصر در هر صفحه است. کمتر از 10 — درخواست‌های بیش از حد مکرر به API و اسکرول ناپیوسته. بیشتر از 25 — بارگذاری طولانی اولین تکه در شبکه‌های کند.

برای تصاویر و ویدئو، اندازه صفحه به 5-10 کاهش می‌یابد، زیرا هر عنصر به زمان اضافی برای بارگذاری رسانه نیاز دارد. برای لیست‌های دارای عناصر متنی (نظرات، لاگ‌ها) اندازه را می‌توان به 30-50 رکورد افزایش داد. توصیه می‌شود اندازه صفحه از طریق API قابل تنظیم باشد تا کلاینت بتواند با شرایط مختلف شبکه سازگار شود.

سوالات متداول

صفحه‌بندی به زبان ساده چیست؟

صفحه‌بندی بارگذاری داده‌ها به صورت تکه‌تکه است، نه یکباره. مثل کتاب: یک صفحه را می‌خوانید، بعد به صفحه بعدی می‌روید. در اپلیکیشن این یعنی وقتی لیست را اسکرول می‌کنید، تکه بعدی داده بارگذاری می‌شود، نه کل لیست یکباره، که در ترافیک و حافظه صرفه‌جویی می‌کند.

تفاوت Offset با Cursor در صفحه‌بندی چیست؟

Offset رکوردها را می‌شمارد: «۲۰ تا را رد کن، ۱۰ تای بعدی را برگردان». اگر بین بارگذاری‌ها رکورد جدیدی اضافه شود — شماره‌بندی به هم می‌ریزد. Cursor از شناسه یکتای آخرین رکورد استفاده می‌کند: «۱۰ رکورد بعد از ID = 100 را برگردان». رکوردهای جدید روی موقعیت تأثیر نمی‌گذارند.

اندازه بهینه صفحه در صفحه‌بندی چقدر است؟

برای اپلیکیشن‌های موبایل 10-25 عنصر بهینه است. برای لیست‌های دارای تصویر — 5-10، برای فیدهای متنی — 20-30. اندازه به اندازه متوسط هر عنصر بستگی دارد: هر چه عنصر سنگین‌تر باشد، صفحه برای نمایش سریع باید کوچک‌تر باشد.

چگونه صفحه‌بندی را در RecyclerView پیاده‌سازی کنیم؟

از کتابخانه Paging 3 از Android Jetpack استفاده کنید. این کتابخانه PagingSource برای بارگذاری، PagingData برای جریان واکنش‌گرا و PagingDataAdapter برای بارگذاری خودکار هنگام اسکرول فراهم می‌کند. کتابخانه از صفحه‌بندی Offset، Cursor و Keyset از طریق PagingSource سفارشی پشتیبانی می‌کند.

اسکرول بی‌نهایت چیست و چه تفاوتی با صفحه‌بندی دارد؟

اسکرول بی‌نهایت — الگوی UI است که در آن تکه جدید داده هنگام نزدیک شدن به انتهای لیست به طور خودکار بارگذاری می‌شود. صفحه‌بندی — مکانیزم بارگذاری داده به صورت تکه‌تکه است، و اسکرول بی‌نهایت یکی از روش‌های نمایش آن است. جایگزین — دکمه «بارگذاری بیشتر».

خلاصه

  • صفحه‌بندی — تکنیک بارگذاری داده به صورت تکه‌تکه، ضروری برای اپلیکیشن‌های موبایل با هر نوع لیستی.
  • صفحه‌بندی Offset در پیاده‌سازی ساده است، اما از ناسازگاری در درج‌ها و کاهش عملکرد در OFFSETهای بزرگ رنج می‌برد.
  • صفحه‌بندی Cursor از شناسه یکتا برای ناوبری استفاده می‌کند — در هر حجمی پایدار و کارآمد است.
  • صفحه‌بندی Keyset بر اساس کلید اصلی فیلتر می‌کند و با استفاده از ایندکس‌ها حداکثر عملکرد را تضمین می‌کند.
  • صفحه‌بندی Time-based رکوردها را بر اساس برچسب‌های زمانی گروه‌بندی می‌کند — ایده‌آل برای فیدهای زمانی و شبکه‌های اجتماعی.
  • انتخاب روش به ماهیت داده بستگی دارد: برای پویا — Cursor/Keyset، برای ایستا — Offset، برای فیدها — Time-based.
  • Paging 3 در Android و راه‌حل‌های استاندارد cursor در iOS/وب زیرساخت آماده برای هر نوع صفحه‌بندی فراهم می‌کنند.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید