使用Vue构建可重用的分页组件

Web应用程序中资源分页不仅对性能很有帮助,而且从用户体验的角度来说也是非常有用的。在这篇文章中,将了解如何使用Vue创建动态和可用的分页组件。

基本结构

分页组件应该允许用户访问第一个和最后一个页面,向前和向后移动,并直接切换到近距离的页面。

大多数应用程序在用户每次更改页面时都会发出API请求。我们需要确保组件允许这样做,但是我们不希望在组件内发出这样的请求。这样,我们将确保组件在整个应用程序中是可重用的,并且请求都是在操作或服务层中进行的。我们可以通过使用用户单击的页面的数字触发事件来实现此目的。

有几种可能的方法来实现API端点上的分页。对于这个例子,我们假设API告诉我们每个页面的结果数、页面总数和当前页面。这些将是我们的动态 props 。

相反,如果API只告诉记录的总数,那么我们可以通过将结果的数量除以每一页的结果数来计算页数: totalResults / resultsPerPage 。

我们想要渲染一个按钮到 第一页 、 上一页 、 页面数量范围 、 下一页 和 最后一页 :

[first] [next] [1] [2] [3] [previous] [last]

比如像下图这样的一个效果:

尽管我们希望渲染一个系列的页面,但并不希望渲染所有可用页面。让我们允许在我们的组件中设置一个最多可见按钮的 props 。

既然我们知道了我们想要的组件要做成什么,需要哪些数据,我们就可以设置HTML结构和所需要的 props 。

<template id="pagination">
  <ul class="pagination">
    <li>
      <button type="button">« First</button>
    </li>
    <li>
      <button type="button">«</button>
    </li>
    <!-- 页数的范围 -->
    <li>
      <button type="button">Next »</button>
    </li>
    <li>
      <button type="button">»</button>
    </li>
  </ul>
</template>
Vue.component('pagination', {
  template: '#pagination',
  props: {
    maxVisibleButtons: {
      type: Number,
      required: false,
      default: 3
    },
    totalPages: {
      type: Number,
      required: true
    },
    total: {
      type: Number,
      required: true
    },
    currentPage: {
      type: Number,
      required: true
    }
  }
})

上面的代码注册了一个 pagination 组件,如果调用这个组件:

<div id="app">
  <pagination></pagination>
</div>

这个时候看到的效果如下:

注意,为了能让组件看上去好看一点,给组件添加了一点样式。

事件监听

现在我们需要通知父组件,当用户单击按钮时,用户点击了哪个按钮。

我们需要为每个按钮添加一个事件监听器。 v-on 指令 允许侦听DOM事件。在本例中,我将使用 v-on 的快捷键 来侦听单击事件。

为了通知父节点,我们将使用 $emit 方法 来发出一个带有页面点击的事件。

我们还要确保分页按钮只有在页面可用时才唯一一个当前状态。为了这样做,将使用 v-bind 将 disabled 属性的值与当前页面绑定。我们还是使用 :v-bind 的快捷键 : 。

为了保持我们的 template 干净,将使用 computed 属性 来检查按钮是否被禁用。使用 computed 也会被缓存,这意味着只要 currentPage 不会更改,对相同计算属性的几个访问将返回先前计算的结果,而不必再次运行该函数。

<template id="pagination">
  <ul class="pagination">
    <li>
      <button type="button" @click="onClickFirstPage" :disabled="isInFirstPage">« First</button>
    </li>
    <li>
      <button type="button" @click="onClickPreviousPage" :disabled="isInFirstPage">«</button>
    </li>
    <li v-for="page in pages">
      <button type="button" @click="onClickPage(page.name)" :disabled="page.isDisabled"> {{ page.name }}</button>
    </li>
    <li>
      <button type="button" @click="onClickNextPage" :disabled="isInLastPage">Next »</button>
    </li>
    <li>
      <button type="button" @click="onClickLastPage" :disabled="isInLastPage">»</button>
    </li>
  </ul>
</template>

Vue.component('pagination', {
  template: '#pagination',
  props: {
    maxVisibleButtons: {
      type: Number,
      required: false,
      default: 3
    },
    totalPages: {
      type: Number,
      required: true
    },
    total: {
      type: Number,
      required: true
    },
    currentPage: {
      type: Number,
      required: true
    }
  },
  computed: {
    isInFirstPage: function () {
      return this.currentPage === 1
    },
    isInLastPage: function () {
      return this.currentPage === this.totalPages
    }
  },
  methods: {
    onClickFirstPage: function () {
      this.$emit('pagechanged', 1)
    },
    onClickPreviousPage: function () {
      this.$emit('pagechanged', this.currentPage - 1)
    },
    onClickPage: function (page) {
      this.$emit('pagechanged', page)
    },
    onClickNextPage: function () {
      this.$emit('pagechanged', this.currentPage + 1)
    },
    onClickLastPage: function () {
      this.$emit('pagechanged', this.totalPages)
    }
  }
})

在调用 pagination 组件时,将 totalPages 和 total 以及 currentPage 传到组件中:

<div id="app">
  <pagination :total-pages="11" :total="120" :current-page="currentPage"></pagination>
</div>

let app = new Vue({
  el: '#app',
  data () {
    return {
      currentPage: 2
    }
  }
})

运行上面的代码,将会报错:

不难发现,在 pagination 组件中,咱们还少了 pages 。从前面介绍的内容,我们不难发现,需要计算出 pages 的值。

Vue.component('pagination', {
  template: '#pagination',
  props: {
    maxVisibleButtons: {
      type: Number,
      required: false,
      default: 3
    },
    totalPages: {
      type: Number,
      required: true
    },
    total: {
      type: Number,
      required: true
    },
    currentPage: {
      type: Number,
      required: true
    }
  },
  computed: {
    isInFirstPage: function () {
      return this.currentPage === 1
    },
    isInLastPage: function () {
      return this.currentPage === this.totalPages
    },
    startPage: function () {
      if (this.currentPage === 1) {
        return 1
      }
      if (this.currentPage === this.totalPages) {
        return this.totalPages - this.maxVisibleButtons + 1
      }
      return this.currentPage - 1
    },
    endPage: function () {
      return Math.min(this.startPage + this.maxVisibleButtons - 1, this.totalPages)
    },
    pages: function () {
      const range = []
      for (let i = this.startPage; i <= this.endPage; i+=1) {
        range.push({
          name: i,
          isDisabled: i === this.currentPage
        })
      }
      return range
    }
  },
  methods: {
    onClickFirstPage: function () {
      this.$emit('pagechanged', 1)
    },
    onClickPreviousPage: function () {
      this.$emit('pagechanged', this.currentPage - 1)
    },
    onClickPage: function (page) {
      this.$emit('pagechanged', page)
    },
    onClickNextPage: function () {
      this.$emit('pagechanged', this.currentPage + 1)
    },
    onClickLastPage: function () {
      this.$emit('pagechanged', this.totalPages)
    }
  }
})

这个时候得到的结果不再报错,你在浏览器中将看到下图这样的效果:

添加样式

现在我们的组件实现了最初想要的所有功能,而且添加了一些样式,让它看起来更像一个分页组件,而不仅像是一个列表。

我们还希望用户能够清楚地识别他们所在的页面。让我们改变表示当前页面的按钮的颜色。

为此,我们可以使用对象语法将HTML类绑定到当前页面按钮上。当使用对象语法绑定类名时,Vue将在值发生变化时自动切换类。

虽然 v-for 中的每个块都可以访问父作用域范围,但是我们将使用 method 来检查页面是否处于 active 状态,以便保持我们的 templage 干净。

Vue.component('pagination', {
  template: '#pagination',
  props: {
    maxVisibleButtons: {
      type: Number,
      required: false,
      default: 3
    },
    totalPages: {
      type: Number,
      required: true
    },
    total: {
      type: Number,
      required: true
    },
    currentPage: {
      type: Number,
      required: true
    }
  },
  computed: {
    isInFirstPage: function () {
      return this.currentPage === 1
    },
    isInLastPage: function () {
      return this.currentPage === this.totalPages
    },
    startPage: function () {
      if (this.currentPage === 1) {
        return 1
      }
      if (this.currentPage === this.totalPages) {
        return this.totalPages - this.maxVisibleButtons + 1
      }
      return this.currentPage - 1
    },
    endPage: function () {
      return Math.min(this.startPage + this.maxVisibleButtons - 1, this.totalPages)
    },
    pages: function () {
      const range = []
      for (let i = this.startPage; i <= this.endPage; i+=1) {
        range.push({
          name: i,
          isDisabled: i === this.currentPage
        })
      }
      return range
    }
  },
  methods: {
    onClickFirstPage: function () {
      this.$emit('pagechanged', 1)
    },
    onClickPreviousPage: function () {
      this.$emit('pagechanged', this.currentPage - 1)
    },
    onClickPage: function (page) {
      this.$emit('pagechanged', page)
    },
    onClickNextPage: function () {
      this.$emit('pagechanged', this.currentPage + 1)
    },
    onClickLastPage: function () {
      this.$emit('pagechanged', this.totalPages)
    },
    isPageActive: function (page) {
      return this.currentPage === page;
    }
  }
})

接下来,在 pages 中添加当前状态:

<li v-for="page in pages">
  <button type="button" @click="onClickPage(page.name)" :disabled="page.isDisabled" :class="{active: isPageActive(page.name)}"> {{ page.name }}</button>
</li>

这个时候你看到效果如下:

但依然还存在一点点小问题,当你在点击别的按钮时, active 状态并不会随着切换:

继续添加代码改变其中的效果:

let app = new Vue({
  el: '#app',
  data () {
    return {
      currentPage: 2
    }
  },
  methods: {
    onPageChange: function (page) {
      console.log(page)
      this.currentPage = page;
    }
  }
})

在调用组件时:

<div id="app">
  <pagination :total-pages="11" :total="120" :current-page="currentPage" @pagechanged="onPageChange"></pagination>
</div>

这个时候的效果如下了:

到这里,基本上实现了咱想要的分页组件效果。

无障碍化处理

熟悉Bootstrap的同学都应该知道,Bootstrap中的组件都做了无障碍化的处理,就是在组件中添加了WAI-ARIA相关的设计。比如在分页按钮上添加 aria-label 相关属性:

在我们这个组件中,也相应的添加有关于WAI-ARIA相关的处理:

<template id="pagination">
  <ul class="pagination" aria-label="Page navigation">
    <li>
      <button type="button" @click="onClickFirstPage" :disabled="isInFirstPage" aria-label="Go to the first page">« First</button>
    </li>
    <li>
      <button type="button" @click="onClickPreviousPage" :disabled="isInFirstPage" aria-label="Previous">«</button>
    </li>
    <li v-for="page in pages">
      <button type="button" @click="onClickPage(page.name)" :disabled="page.isDisabled" :aria-label="`Go to page number ${page.name}`"> {{ page.name }}</button>
    </li>
    <li>
      <button type="button" @click="onClickNextPage" :disabled="isInLastPage" aria-label="Next">Next »</button>
    </li>
    <li>
      <button type="button" @click="onClickLastPage" :disabled="isInLastPage" aria-label="Go to the last page">»</button>
    </li>
  </ul>
</template>

这样有关于 aria 相关的属性就加上了:

最终的效果如下,可以点击下面的连接访问:

https://codepen.io/airen/pen/mxMLrG

您可能感兴趣的文章:

  • 基于Vue如何封装分页组件
  • Vue.js实现一个自定义分页组件vue-paginaiton
  • vue分页组件table-pagebar使用实例解析
  • 使用vue.js制作分页组件
  • 基于Vue.js的表格分页组件
(0)

相关推荐

  • 基于Vue.js的表格分页组件

    一.Vue.js简介 1.Vue的主要特点: (1) 简洁 (2) 轻量 (3)快速 (4) 数据驱动 (5) 模块友好 (6) 组件化 (1) 简洁 下面看一段Angular的实现双向绑定的代码 // html <body ng-app="myApp"> <div ng-controller="myCtrl"> <p>{{ note }}</p> <input type="text" ng-

  • 基于Vue如何封装分页组件

    使用Vue做双向绑定的时候,可能经常会用到分页功能 接下来我们来封装一个分页组件 先定义样式文件 pagination.css ul, li { margin: 0px; padding: 0px; } .page-bar { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-se

  • 使用vue.js制作分页组件

    学习了vue.js一段时间,拿它来做2个小组件,练习一下. 我这边是用webpack进行打包,也算熟悉一下它的运用. 源码放在文末的 github 地址上. 首先是index.html <!DOCTYPE html> <html> <head> <title>Page</title> <style type="text/css"> * { margin: 0; padding: 0; font-family: 'O

  • Vue.js实现一个自定义分页组件vue-paginaiton

    vue实现一个分页组件vue-paginaiton vue使用了一段时间的感触就是,我再也不想直接操作DOM了.数据绑定式的编程体验真是好.实现的一个分页组件. 这里的css就不放出来了,可以看直接去github上下载:vue-pagination 先上一张实例图吧 模版 <div class="page-bar"> <ul> <li v-if="showFirstText"><a v-on:click="cur-

  • vue分页组件table-pagebar使用实例解析

    之前一直接触都是原始的前端模型,jquery+bootstrap,冗杂的dom操作,繁琐的更新绑定.接触vue后,对前端MVVM框架有了全新的认识.本文是基于webpack+vue构建,由于之前的工作主要是基于java的服务端开发工作,对前端框架和组件的理解,不够深入,借此来记录在前端框架使用和构建中的点点滴滴. 此分页组件参照于bootstrap-datatable底部分页开发完成,相关参数增加自定义功能. 最终使用展现效果图如下,数据来源于cnodejs[https://cnodejs.or

  • 使用Vue构建可重用的分页组件

    Web应用程序中资源分页不仅对性能很有帮助,而且从用户体验的角度来说也是非常有用的.在这篇文章中,将了解如何使用Vue创建动态和可用的分页组件. 基本结构 分页组件应该允许用户访问第一个和最后一个页面,向前和向后移动,并直接切换到近距离的页面. 大多数应用程序在用户每次更改页面时都会发出API请求.我们需要确保组件允许这样做,但是我们不希望在组件内发出这样的请求.这样,我们将确保组件在整个应用程序中是可重用的,并且请求都是在操作或服务层中进行的.我们可以通过使用用户单击的页面的数字触发事件来实现

  • Vue.js分页组件实现:diVuePagination的使用详解

    一.介绍 Vue.js 是什么 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合.另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动. 二.创建初始化项目 这里不在详细说明,我们的分页演示只需要vue和vue-router就可以了,我们直接构建项目和设置配置. main.js:

  • 尝试自己动手用react来写一个分页组件(小结)

    本文介绍了尝试自己动手用react来写一个分页组件(小结),分享给大家,具体如下: 分页效果 在线预览 github地址 效果截图(样式可自行修改): 构建项目 create-react-app react-paging-component 分页组件 1.子组件 创建 Pagecomponent.js 文件 核心代码: 初始化值 constructor(props) { super(props) this.state = { currentPage: 1, //当前页码 groupCount:

  • 基于vue实现swipe分页组件实例

    项目背景 图片轮播是前端项目必有项,当前有很多效果很酷炫的轮播插件,例如 Swiper . 但是当我们项目中的图片轮播只需要一个很简单的轮播样式,比如这样的 我们引用这样一个 110k 的大插件,就大材小用了.再安利一下,swiper2.x和swiper3.x对移动和PC端支持情况如下图 当当当当~~~ 我们今天的主角登场了, thebird/Swipe,这个插件完成了图片轮播需要的基本功能,只有 14.2k ,真真的 轻量级 啊.还有,还有 翻译一下,就是俺们全支持,不管你是PC端(IE7+)

  • VUE实现一个分页组件的示例

    分页是WEB开发中很常用的功能,尤其是在各种前后端分离的今天,后端API返回数据,前端根据数据的count以及当前页码pageIndex来计算分页页码并渲染到页面上已经是一个很普通很常见的功能了.从最开始的jquery时代到现在的各种各样的前端框架时代,分页功能都是必不可少的. 分页大多数(基本上)情况下都是对异步数据列表的处理,这里首先需要明白一下分页的流程. 在已知每页显示数据量pageSize以及当前页码pageIndex的情况下: 请求API,返回第一屏数据(pageSize内)以及所有

  • 一个可复用的vue分页组件

    不废话,先上组件文件pages.vue: <template> <div class="pages-box" v-if="pageTotal > 0"> <ul class="pages"> <li class="pages-prev"> <a v-if="pageNow != 1" href="javascript:void(0);&q

随机推荐