vue + element-ui的分页问题实现


Posted in Javascript onDecember 17, 2018

背景介绍

最近比较空闲,公司的后台就想着把现在的后台管理系统给改版一下,说是以前的太难看了,用着也不好用,然后给我甩过来一个ant-design-pro的链接,说是他看这个就挺不错的。

我当时心里就想着,之前的那个项目混合在你们的java项目里,跟普通的jsp页面差不多,一下就是一大堆的css和js文件,看着我都害怕(好吧,我承认其实我都不敢看),这能加载的快了就奇了怪了。ant-design最初是为react设计的,ant-design-pro自然也是用react了,不得不说人家这个界面看着确实舒服。

对着ant-design-pro的官方文档看了一通,貌似看了跟没看也差不多???算了,还是直接看代码吧,整理了一下思路,大致上是看懂了,除了react + react-router外,状态管理用的是 dva, redux的异步问题算是解决了,要不就开始直接写页面吧?

等等,我好像漏掉了点什么?噢,对,先看看打包出来的文件大小,一打包我的心就凉了,最大的js居然有900多k,ant-design的源文件是真的大。react我还只是能写出代码,打包优化这个可就有点为难我了。这时的我再想到公司那1m的带宽,还有这几个后台的技术能力,要不然这个技术栈我还是放弃吧?不能指望连 请求头, CORS稍微高级一点的携带cookie, nginx静态服务器 都搞不懂的人去给我弄个静态服务器,再顺便开启一下gzip吧?算了算了,找找有没有vue + element-ui的后台模板,不用太费劲就找到了 vue-element-admin 。

vue-element-admin用着还行,就是界面不太符合我的理想情况,就对着ant-design-pro改造了一点,列表页大概就是下面这样了。列表的数据是要分页的,普通的列表页只有一个页面栈,也就是用户点击地址栏的回退地址栏时,会返回上一个页面栈,而不是上一页的数据,不太符合用户习惯吧?毕竟传统的网站都是可以回退到上一页的,嗯,话不多说,进入正题吧。

vue + element-ui的分页问题实现 

第一步:改变地址栏

假设列表页的路径是 /user/list,分页相关的参数为 { page: 1, pagesize: 10 } ,从其他页面跳转过来的时候,我们的路径通常是不包含任何参数的,之后的列表数据都是根据该页面的page和pagesize进行变化的,当未使用keep-alive缓存组件时,每次进入列表页都相当于第一次进入,也就是说每次都只能获取第一页的数据。

既然列表数据是用page和pagesize进行变化的,那直接从地址栏获取page和pagesize进行赋值不就好了?那么是改变地址栏的代码是直接写在当前页面还是 独立为分页组件 呢?从复用性方面来说,还是独立出来的好,毕竟其他页面可能也会使用到,总不能每次都复制粘贴吧,那组件化的意义何在?当然了,也不是说分页就必须用这个自定义的分页组件,只推荐在 主页面(非遮罩层 ,有的页面会在点击某一行数据时出现遮罩层显示子列表,此时使用element-ui的分页组件即可)需要分页时使用。

当改变地址栏的时候,我们是不希望不带分页参数的页面栈存在的,此时用replace直接替换即可。

MyPagination.vue的初始结构为:

<template>
 <div class = " flex all-center">
 <template v-if="total > 0">
  <el-pagination
  :page-size="pagesize"
  :total="total"
  :current-page="page"
  background
  layout="prev, pager, next, jumper, total"
  class="my-pagination"
  @current-change="changePage" />
 </template>
 </div>
</template>

<script>
export default {
 name: 'MyPagination',
 props: {
 total: {
  type: Number,
  default: 0,
 },
 page: {
  type: Number,
  default: 1,
 },
 pagesize: {
  type: Number,
  default: 10,
 },
 totalPages: {
  type: Number,
  default: 1,
 },
 },
 created() {
 this.getCurrentPage();
 },
 methods: {
 changePage(val) {
  this.handlePage('push', val, this.pagesize);
  this.$emit('change', val, this.pagesize);
 },
 getCurrentPage() {
  var { page, pagesize } = this.$route.query;
  if (!page || !pagesize) {
  this.handlePage('replace', page || 1, +pagesize || this.pagesize);
  return true;
  }
  return false;
 },
 handlePage(type, page, pagesize) {
  this.$router[type]({
  path: this.$route.path,
  query: { ...this.$route.query, page, pagesize },
  });
 }
 },
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.my-pagination {
 padding-top: 24px; 
}
</style>

父组件的关键代码:

<MyPagination :total = "total" :pagesize = "pagesize" :page="page" :totalPages = "totalPages" @change = "changePage" />

methods: {
 changePage(page, pagesize) {
  var _page = this.page,
   _pagesize = this.pagesize;
  this.page = page;
  this.pagesize = pagesize;
  if (page !== _page && pagesize || _pagesize !== pagesize) this.fetchData(); // 非首次进入页面时再获取分页数据,因为在created钩子中已经获取过一次了。
 },
}

实现效果: 首次进入该页面时,如果不含有分页参数,就会先改变分页参数,然后再获取数据,之后点击分页组件的页码也会获取分页之后的数据。

第二步: 观察路由变化

上一步的实现效果乍一看好像没什么不对劲的地方,但是如果直接改变地址栏的话,显示的当前页和当前数据都不会变化。前端路由在页面的查询参数(指的是 router的查询参数 ,可不是普通页面的查询参数)变化时,默认是不会重新加载的,除非页面的key发生变化,这样是为了尽可能的防止页面重新渲染,所以就不用key的方式解决了,直接通过vue的watch检测 $route 的变化,从而改变当前页和当前数据的显示问题。

在MyPagination.vue中新增:

watch: {
 '$route'(to, from) {
  let { page, pagesize } = to.query;
  if (!this.getCurrentPage()) {
  this.$emit('change', +page || 1, +pagesize || 10);
  }
 }
},

第三步: 控制pagesize的大小

在上一步的效果中,当改变地址栏的page和pagesize时,列表页的数据也会随之变化。既然是根据地址栏的参数变化,那么新的问题就产生了,

如果用户输入的page大于页面总数呢?

这个时候主要就看后台怎么设计了,

返回第一页的数据。

getCurrentPage() {
 var { page, pagesize } = this.$route.query;
 /* 
 (totalPages > 0 && (page > totalPages));满足总页数大于0且当前页大于总页数时,跳转到第一页
 */
 if (!page || !pagesize || (totalPages > 0 && (page > totalPages))) {
 this.handlePage('replace', page || 1, this.pagesize);
 return true;
 }
 return false;
},

返回最后一页的数据(我觉得这种操作应该是比较合理的)。

getCurrentPage() {
 var { page, pagesize } = this.$route.query,
  MAX_PAGESIZE = this.max,
  totalPages = this.totalPages;
 if (!page || !pagesize) {
 this.handlePage('replace', page || 1, +pagesize || this.pagesize);
 return true;
 } else if (totalPages > 0 && (page > totalPages)) {
 this.handlePage('replace', totalPages, +pagesize);
 return true;
 }
 return false;
},

替换当前页面栈,return true的作用是阻止watch中的后续操作,取消本次请求。替换页面以后,请求远程数据,更新当前页和数据的显示。

返回空数组(可能大多数后台都是这么设计的,他们应该没想过page会大于总页数吧)。 代码与2中的一样。

上文都是建立在totalPages已确定的情况,如果是首次进入页面的话情况就会不一样了。

如果是首次进入页面的话,totalPages第一次是0,也就是地址栏的参数将不会发生变化,这时候就会出现地址栏和分页组件的显示不一致的情况。这时候可以在分页组件中watch totalPages的变化。

totalPages(newVal, oldVal) {
 if (+oldVal === 0 && newVal > 0) {
 this.handlePage('replace', this.page, +this.pagesize);
 }
}

如果pagesize过大呢?

pagesize是必须要进行限制的,如果太大的话,后台查询数据就会非常慢,也可能会造成压力。 解决办法其实也简单,就是在props增加一个max属性,然后在getCurrentPage方法中进行限制,代码如下:

props: {
 max: {
  type: Number,
  default: 20,
 },
},
methods: {
 getCurrentPage() {
  var { page, pagesize } = this.$route.query,
   MAX_PAGESIZE = this.max,
   totalPages = this.totalPages;
  if (!page || !pagesize) {
  this.handlePage('replace', page || 1, +pagesize || this.pagesize);
  return true;
  } else if (pagesize > MAX_PAGESIZE) {
  this.handlePage('replace', page, MAX_PAGESIZE);
  return true;
  } else if (totalPages > 0 && (page > totalPages)) {
  this.handlePage('replace', totalPages, +pagesize);
  return true;
  }
  return false;
 },
},

第四步: 优化代码

点击分页组件的页码时产生两次请求

点击分页组件时,1. 会监听current-change事件并改变地址栏,同时emit change事件至父组件,2. 但是地址栏改变后,在watch $route也会emit change事件至父组件,那么只需要合并emit change事件,即current-change事件中只改变地址栏。

changePage(val) {
 this.handlePage('push', val, this.pagesize);
},

结果

至此,一个自定义的分页组件就已经实现了,改变地址栏的参数就可以看到分页数据的变化了,点击页码时地址栏也会随之而改变,请求数量已经尽可能的减少了。

自定义的分页组件: MyPagination.vue

列表页: list.vue

完整demo: front_end

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Function.prototype.bind用法示例
Sep 16 Javascript
json数据与字符串的相互转化示例
Sep 18 Javascript
购物车选中得到价格实现示例
Jan 26 Javascript
原生js事件的添加和删除的封装
Jul 01 Javascript
javascript数据类型示例分享
Jan 19 Javascript
JavaScript制作简易的微信打飞机
Mar 31 Javascript
jQuery自动添加表单项的方法
Jul 13 Javascript
javascript闭包概念简单解析(推荐)
Jun 03 Javascript
javascript中replace使用方法总结
Mar 01 Javascript
thinkjs 文件上传功能实例代码
Nov 08 Javascript
解决vue处理axios post请求传参的问题
Mar 05 Javascript
微信小程序位置授权处理方法
Jun 13 Javascript
Vue 中的受控与非受控组件的实现
Dec 17 #Javascript
js实现移动端轮播图
Dec 21 #Javascript
微信小程序登录按钮遮罩浮层效果的实现方法
Dec 16 #Javascript
微信小程序 JS动态修改样式的实现方法
Dec 16 #Javascript
Echart折线图手柄触发事件示例详解
Dec 16 #Javascript
vue使用pdfjs显示PDF可复制的实现方法
Dec 14 #Javascript
antd Upload 文件上传的示例代码
Dec 14 #Javascript
You might like
DC动画很好看?新作烂得令人发指,名叫《红色之子》
2020/04/09 欧美动漫
经典的星际争霸,满是回忆的BGM
2020/04/09 星际争霸
解析PHP中empty is_null和isset的测试
2013/06/29 PHP
destoon调用企业会员公司形象图片的实现方法
2014/08/21 PHP
PHP缓存集成库phpFastCache用法
2014/12/15 PHP
PHP中字符安全过滤函数使用小结
2015/02/25 PHP
YII2框架中ActiveDataProvider与GridView的配合使用操作示例
2020/03/18 PHP
Javascript模块模式分析
2008/05/16 Javascript
JS 创建对象(常见的几种方法)
2008/11/03 Javascript
JavaScript 组件之旅(二)编码实现和算法
2009/10/28 Javascript
jquery ajax学习笔记2 使用XMLHttpRequest对象的responseXML
2011/10/16 Javascript
html dom节点操作(获取/修改/添加或删除)
2014/01/23 Javascript
jquery实现pager控件示例
2014/04/09 Javascript
js判断当前浏览器类型,判断IE浏览器方法
2014/06/02 Javascript
JS实现兼容性较好的随屏滚动效果
2015/11/09 Javascript
angular.js之路由的选择方法
2016/09/24 Javascript
使用微信小程序开发前端【快速入门】
2016/12/05 Javascript
详解使用vue-router进行页面切换时滚动条位置与滚动监听事件
2017/03/08 Javascript
JavaScript数组方法的错误使用例子
2018/09/13 Javascript
ES6入门教程之Array.from()方法
2019/03/23 Javascript
vue+element模态框中新增模态框和删除功能
2019/06/11 Javascript
django自定义Field实现一个字段存储以逗号分隔的字符串
2014/04/27 Python
Python中为feedparser设置超时时间避免堵塞
2014/09/28 Python
Numpy中矩阵matrix读取一列的方法及数组和矩阵的相互转换实例
2018/07/02 Python
flask入门之表单的实现
2018/07/18 Python
python TKinter获取文本框内容的方法
2018/10/11 Python
解决Python下imread,imwrite不支持中文的问题
2018/12/05 Python
Python 实现中值滤波、均值滤波的方法
2019/01/09 Python
Python设计模式之职责链模式原理与用法实例分析
2019/01/11 Python
python实现在cmd窗口显示彩色文字
2019/06/24 Python
python 使用raw socket进行TCP SYN扫描实例
2020/05/05 Python
UNIX文件系统分类
2014/11/11 面试题
红旗渠导游词
2015/02/09 职场文书
2015年数学教师工作总结
2015/05/20 职场文书
MySQL的InnoDB存储引擎的数据页结构详解
2022/03/03 MySQL
Python实现双向链表
2022/05/25 Python