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 相关文章推荐
Lazy Load 延迟加载图片的jQuery插件中文使用文档
Oct 18 Javascript
jquery修改属性值实例代码(设置属性值)
Jan 06 Javascript
jquery实现类似EasyUI的页面布局可改变左右的宽度
Sep 12 Javascript
javascript制作照片墙及制作过程中出现的问题
Apr 04 Javascript
javascript jquery对form元素的常见操作详解
Jun 12 Javascript
AngularJS入门教程引导程序
Aug 18 Javascript
AngularJS入门教程之Cookies读写操作示例
Nov 02 Javascript
angular-ngSanitize模块-$sanitize服务详解
Jun 13 Javascript
基于Vue生产环境部署详解
Sep 15 Javascript
jQuery模拟12306城市选择框功能简单实现方法示例
Aug 13 jQuery
javascript实现摄像头拍照预览
Sep 30 Javascript
微信小程序在text文本实现多种字体样式
Nov 08 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
IIS6+PHP5+MySQL5+Zend Optimizer+phpMyAdmin安装配置图文教程 2009年
2009/06/08 PHP
php生成txt文件标题及内容的方法
2014/01/16 PHP
深入解析PHP的Laravel框架中的event事件操作
2016/03/21 PHP
Laravel等框架模型关联的可用性浅析
2019/12/15 PHP
JavaScript 的方法重载效果
2009/08/07 Javascript
JQuery Ajax 跨域访问的解决方案
2010/03/12 Javascript
JavaScript获取/更改文本框的值的实例代码
2013/08/02 Javascript
JavaScript fontsize方法入门实例(按照指定的尺寸来显示字符串)
2014/10/17 Javascript
javascript 事件处理示例分享
2014/12/31 Javascript
jquery使用each方法遍历json格式数据实例
2015/05/18 Javascript
JavaScript多图片上传案例
2015/09/28 Javascript
JS+CSS相对定位实现的下拉菜单
2015/10/06 Javascript
jquery+json实现动态商品内容展示的方法
2016/01/14 Javascript
js中实现字符串和数组的相互转化详解
2016/01/24 Javascript
基于nodejs 的多页面爬虫实例代码
2017/05/31 NodeJs
基于daterangepicker日历插件使用参数注意的问题
2017/08/10 Javascript
JS逻辑运算符短路操作实例分析
2018/07/09 Javascript
react 组件传值的三种方法
2019/06/03 Javascript
微信小程序嵌入腾讯视频源过程详解
2019/08/08 Javascript
vue实现五子棋游戏
2020/05/28 Javascript
Python自定义函数的创建、调用和函数的参数详解
2014/03/11 Python
使用Python多线程爬虫爬取电影天堂资源
2016/09/23 Python
Python数据结构之单链表详解
2017/09/12 Python
python如何重载模块实例解析
2018/01/25 Python
Python一句代码实现找出所有水仙花数的方法
2018/11/13 Python
快速排序的四种python实现(推荐)
2019/04/03 Python
Python求两点之间的直线距离(2种实现方法)
2019/07/07 Python
探索HTML5本地存储功能运用技巧
2016/03/02 HTML / CSS
管理专员自荐信
2014/01/26 职场文书
公司聘任书模板
2014/03/29 职场文书
班组长竞聘书
2014/03/31 职场文书
教师党的群众路线教育实践活动剖析材料
2014/10/09 职场文书
2015年音乐教研组工作总结
2015/07/22 职场文书
pytorch查看网络参数显存占用量等操作
2021/05/12 Python
MySQL 聚合函数排序
2021/07/16 MySQL
MySQL使用IF语句及用case语句对条件并结果进行判断 
2022/09/23 MySQL