Привет, Хабр! Представляю вашему вниманию перевод статьи "Pagination in Vue.js" автора Denny Headrick.

Пагинация увеличивает UX, позволяя пользователям визуализировать данные в небольших блоках или на страницах. Вот и компонент Vue.js можно сделать с разбивкой по страницам, который позволит нам просматривать только часть наших данных за раз.



Сначала я буду добавлять кусочек за кусочком в свой JavaScript объект. А затем покажу template (шаблон)

Из всех локальных данных, мне нужны, только данные?-?номер страницы.

data(){
    return {
      pageNumber: 0  // по умолчанию 0
    }
}

Для props (свойств), передача данных является обязательной, но я также возьму size аргумент для максимального количества записей.

props:{
    listData:{
      type:Array,
      required:true
    },
    size:{
      type:Number,
      required:false,
      default: 10
    }
}

Для моей реализации я буду использовать методы для перехода на previous (предыдущую) и next (следующую) страницы:

methods:{
      nextPage(){
         this.pageNumber++;
      },
      prevPage(){
        this.pageNumber--;
      }
}

Быстрое вычисляемое свойство computed, чтобы выяснить, сколько есть страниц:

pageCount(){
      let l = this.listData.length,
          s = this.size;
      // редакция переводчика спасибо комментаторам
      return Math.ceil(l/s);
      // оригинал
      // return Math.floor(l/s);
}


Теперь вычисленное свойство (computed) paginatedData?-?это место, где все объединяется. Это отфильтрованные данные, которые будут отображаться.

paginatedData(){
    const start = this.pageNumber * this.size,
          end = start + this.size;
    return this.listData.slice(start, end);
}

Редакция: я изначально делал что-то ужасное и громоздкое, чтобы скопировать массив. Использование .slice?-?лучший подход. Спасибо, Alexander Karelas.

И наш template (шаблон)

<div>
  <ul>
    <li v-for="p in paginatedData">
      {{p.first}} 
      {{p.last}}  
      {{p.suffix}}
    </li>
  </ul>
  <button @click="prevPage">
    Previous
  </button>
  <button @click="nextPage">
    Next
  </button>
</div>

Я хочу, чтобы кнопки работали, когда они только должны. Для кнопки prevPage я добавлю: 
:disabled=«pageNumber==0»
а для кнопки nextPage добавлю: 
:disabled=«pageNumber >= pagecount -1»
Рабочая демонстрация моего компонента:


Иногда бывает трудно переоценить ситуацию, но разбиение на страницы?-?это простая функция, которую мы можем предложить нашим пользователям без особых усилий.

Спасибо за прочтение!

Denny Headrick?-?веб-разработчик USAF, который слишком сильно любит свою работу. В дополнение к разработке на различных платформах и Vue.js, когда он может, он любит вести блог изредка. Вы можете следить за ним в Twitter на @dennythecoder.

Комментарии (18)


  1. OlehKurpiak
    13.07.2018 18:05
    +1

    math.floor(5/2) = 2, хотя количество страниц должно бить 3


    1. BerkutEagle
      13.07.2018 20:01

      В примере получается целое количество страниц, поэтому ошибка не проявилась. Потом сломается, на проде!
      Вот вам и пример необходимости тестов.


      1. VolCh
        14.07.2018 11:46

        Тут тесты сами по себе вряд ли бы помогли, даже со 100% покрытия. Собственно тесты — это и есть примеры использования.


    1. RSM
      14.07.2018 15:26

      Math.ceil должно быть


  1. c01nd01r
    13.07.2018 18:30

    Это, скорее, виджет, который показывает страницы. Пагинация должна как-то реагировать/переключать элементы.
    Можно хотя бы ивенты добавить.


  1. j_wayne
    13.07.2018 18:53

    С «createFakeData» неинтересно. Вот если бы XHR на бэкенд…


  1. vasyan
    13.07.2018 23:25

    пример уж сильно хелувордный. Для нормального разговора нужны:
    1. XHR и обработка момента загрузки
    2. Router — надо page делать параметром в URL
    3. Vuex


    1. codemafia
      14.07.2018 15:11

      Предлагаю поделиться с нами вашим виденьем с примерами.


  1. VolCh
    14.07.2018 11:49

    Общий вопрос по пагинации: если источник данных её не поддерживает, потенциальное число записей не настолько большое, чтобы клиент начал ощутимо тормозить, то есть ли вообще смысл с нею заморачиваться, пока нет жёсткого требования от бизнеса? Лично мне удобнее скроллить, например, таблицу на 1000 строк, чем страничками её листать.


    1. klubben
      14.07.2018 13:11

      Полностью согласен, в 99% случаев пагинация ухудшает UX
      Никогда не понимал зачем делать, например, список товаров по 10 штук на странице, покажите все сразу


      1. man_without_face
        14.07.2018 16:57

        В 99?? Вы пошутили?


  1. KriMs
    15.07.2018 12:02

    :disabled=«pageNumber=0»

    тут должно быть два равно "=="


    1. bad4iz Автор
      15.07.2018 21:35

      спасибо поправил


  1. Yaschik
    15.07.2018 21:30

    :disabled="pageNumber=0"
    Поправте на
    :disabled="pageNumber==0"


    1. bad4iz Автор
      15.07.2018 21:32

      спасибо уже поправил


  1. ivolkoff
    15.07.2018 21:30

    С vue я не понимаю одной вещи, как правильно хранить данные так что бы они были актуальны, например я гружу по ajax данные пачками, с первой страницы перешёл на вторую, а потом снова на первую. и как правильно действовать?
    Брать данные из локального хранилища или снова ajax к серверу. Понимаю что лучше как просит бизнес. Но как вы делаете?


    1. bad4iz Автор
      15.07.2018 21:34

      Здесь все зависит от того что дорого…
      если дорого запросы то локально хранить.
      Если хотим сэкономить место у пользователя то запросы.
      А еще вы не рассмотрели SPA


    1. Odrin
      16.07.2018 11:50

      Все всегда зависит от конкретной задачи. Но зачастую вместе с серверной пагинацией идет еще фильтрация/сортировка и пытаться что-то кэшировать на клиенте уже нецелесообразно, imho оптимальнее всего использовать кэш на уровне сетевого взаимодействия — get запросы отлично кешируются.