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 相关文章推荐
响应鼠标变换表格背景或者颜色的代码
Mar 30 Javascript
JavaScript 上万关键字瞬间匹配实现代码
Jul 07 Javascript
javascript中数组中求最大值示例代码
Dec 18 Javascript
关于jQuery中的each方法(jQuery到底干了什么)
Mar 05 Javascript
jQuery插件boxScroll实现图片轮播特效
Jul 14 Javascript
Easyui form combobox省市区三级联动
Jan 13 Javascript
BootstrapValidator实现注册校验和登录错误提示效果
Mar 10 Javascript
jQuery插件FusionCharts绘制2D环饼图效果示例【附demo源码】
Apr 10 jQuery
dropload.js插件下拉刷新和上拉加载使用详解
Oct 20 Javascript
JS实现的JSON序列化操作简单示例
Jul 02 Javascript
深度解读vue-resize的具体用法
Jul 08 Javascript
基于 Vue 的 Electron 项目搭建过程图文详解
Jul 22 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
PHP中$_FILES的使用方法及注意事项说明
2014/02/14 PHP
PHP JSON出错:Cannot use object of type stdClass as array解决方法
2014/08/16 PHP
PHP 实现页面静态化的几种方法
2017/07/23 PHP
PHP使用Redis实现防止大并发下二次写入的方法
2017/10/09 PHP
一个非常全面的javascript URL解析函数和分段URL解析方法
2014/04/12 Javascript
分享js粘帖屏幕截图到web页面插件screenshot-paste
2020/08/21 Javascript
微信小程序富文本渲染引擎的详解
2017/09/30 Javascript
基于JavaScript表单脚本(详解)
2017/10/18 Javascript
JS和Canvas实现图片的预览压缩和上传功能
2018/03/30 Javascript
javascript触发模拟鼠标点击事件
2019/06/26 Javascript
[02:38]2018DOTA2亚洲邀请赛赛前采访-VGJ.T
2018/04/03 DOTA
python使用pil生成缩略图的方法
2015/03/26 Python
Python聊天室实例程序分享
2016/01/05 Python
对numpy中的数组条件筛选功能详解
2018/07/02 Python
python实现连续图文识别
2018/12/18 Python
解决python3运行selenium下HTMLTestRunner报错的问题
2018/12/27 Python
python 多线程串行和并行的实例
2019/02/22 Python
python 3.6.7实现端口扫描器
2019/09/04 Python
Python 制作查询商品历史价格的小工具
2020/10/20 Python
怎么解决pycharm license Acti的方法
2020/10/28 Python
canvas离屏技术与放大镜实现代码示例
2018/08/31 HTML / CSS
详解HTML5布局和HTML5标签
2020/10/26 HTML / CSS
Get The Label中文官网:英国运动时尚购物平台
2017/04/19 全球购物
Smallable意大利家庭概念店:设计师童装及家居装饰
2018/01/08 全球购物
Perfume’s Club法国站:购买香水和化妆品
2019/05/02 全球购物
服装厂厂长职责
2013/12/16 职场文书
岗位竞聘演讲稿
2014/01/10 职场文书
军训学生自我鉴定
2014/02/12 职场文书
银行行长竞聘演讲稿
2014/04/23 职场文书
幼儿教师求职信
2014/05/24 职场文书
党员群众路线个人整改措施思想汇报
2014/10/12 职场文书
2014年双拥工作总结
2014/11/21 职场文书
会计岗位职责
2015/02/03 职场文书
催款律师函范文
2015/05/27 职场文书
小学远程教育工作总结
2015/08/13 职场文书
2016年6.5世界环境日宣传活动总结
2016/04/01 职场文书