Пагинация — норм. Бесконечная прокрутка — зло, если ею злоупотреблять. Laravel и Vue.js позволяют прикрутить оба варианта за пять минут, но вот выбор, какой юзать, решает, будут ли юзеры материться или останутся довольны.

Скажу честно: я ненавижу бесконечную прокрутку. Вот ты ищешь инфу на сайте, уже нашёл внизу интересное, отвлёкся, вернулся — и, привет, список улетел, потому что скролл перезагрузился. Или хочешь перейти сразу на страницу 10? Да хрен тебе. Листай, как проклятый.

Но да, иногда она реально нужна — например, в фидах соцсетей, где никто не собирается прыгать по страницам. В остальных случаях — старая добрая пагинация рулит. В Laravel и Vue.js можно прикрутить и то, и то. Код простой, но давай разберём, где чаще всего косячат.

Laravel: отдаём данные порциями

Любой список постов или заказов — это paginate(). Ничего умнее и не надо:

public function index(Request $request)
{
    $posts = Post::query()->paginate(10);
    return response()->json($posts);
}

Маршрут:

Route::get('/posts', [PostController::class, 'index']);

Всё. На стороне Laravel это делается быстрее, чем ты откроешь очередную доку.

Vue.js: простая пагинация

В Vue пагинация — это просто кнопки «вперёд/назад» или номера страниц. Логика такая же: отправляем ?page=2, получаем данные. Всё.

<template>
  <div>
    <div v-for="post in posts" :key="post.id">
      {{ post.title }}
    </div>
    <button @click="loadMore" v-if="!loading">Загрузить ещё</button>
    <div v-if="loading">Загрузка...</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      posts: [],
      page: 1,
      loading: false
    }
  },
  methods: {
    async fetchPosts() {
      this.loading = true;
      const response = await fetch(`/api/posts?page=${this.page}`);
      const data = await response.json();
      this.posts.push(...data.data);
      this.loading = false;
    },
    loadMore() {
      this.page++;
      this.fetchPosts();
    }
  },
  mounted() {
    this.fetchPosts();
  }
}
</script>

Это нормальная реализация, и она юзеру даёт выбор — грузить дальше или нет.

Vue.js: бесконечный скролл

А вот тут начинается веселье. Ты вешаешь слушатель на скролл и тупо грузишь новые данные, когда пользователь доходит до конца:

handleScroll() {
  const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
  if (scrollTop + clientHeight >= scrollHeight - 50) {
    this.page++;
    this.fetchPosts();
  }
}

Минусы:

  • баги с дублированием данных;
  • жрёт память, если юзер скроллит до потери пульса;
  • при обновлении страницы всё улетает к чертям.

Да, оно красиво смотрится на демо. Но на проде это превращается в боль, если юзеров тысячи.

Где чаще всего ломается

  • Нет лимита. Юзеру дали возможность скроллить бесконечно → браузер начинает хавать память как не в себя.
  • Нет кнопки «назад к странице N». Всё, человек матерится и закрывает сайт.
  • Дублируются записи. Потому что криво проверяют, что уже загружено.
  • API не оптимизировано. Запросы грузят больше данных, чем надо, и Redis/MySQL начинает задыхаться.

Итог

Laravel и Vue делают пагинацию и скролл лёгкими. Но это не вопрос «как», а вопрос «зачем». Если у тебя блог или интернет-магазин — делай нормальную пагинацию, не мучай людей. Если у тебя соцсеть или фид новостей — скролл норм.