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 相关文章推荐
ie9 提示'console' 未定义问题的解决方法
Mar 20 Javascript
javascript学习笔记之10个原生技巧
May 21 Javascript
浅析webapp框架AngularUI的demo
Dec 21 Javascript
jquery获取css的color值返回RGB的方法
Dec 18 Javascript
最全的Javascript编码规范(推荐)
Jun 22 Javascript
js实现各种复制到剪贴板的方法(分享)
Oct 27 Javascript
javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】
Dec 15 Javascript
js实现适合新闻类图片的轮播效果
Feb 05 Javascript
微信小程序模版渲染详解
Jan 26 Javascript
js中Object.defineProperty()方法的不详解
Jul 09 Javascript
node上的redis调用优化示例详解
Oct 30 Javascript
基于Vue sessionStorage实现保留搜索框搜索内容
Jun 01 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
一个从别的网站抓取信息的例子(域名查询)
2006/10/09 PHP
PHP Session_Regenerate_ID函数双释放内存破坏漏洞
2011/01/27 PHP
PHP header函数分析详解
2011/08/06 PHP
laravel-admin 中列表筛选方法
2019/10/03 PHP
js 模拟气泡屏保效果代码
2010/07/10 Javascript
jQuery拖拽 &amp; 弹出层 介绍与示例
2013/12/27 Javascript
js在输入框屏蔽按键,只能键入数字的示例代码
2014/01/03 Javascript
深入理解JSON数据源格式
2014/01/10 Javascript
JavaScript中判断整字类型最简洁的实现方法
2014/11/08 Javascript
JS实现在线统计一个页面内鼠标点击次数的方法
2015/02/28 Javascript
jquery实现图片左右切换的方法
2015/05/07 Javascript
WEB前端开发都应知道的jquery小技巧及jquery三个简写
2015/11/15 Javascript
JS刷新父窗口的几种方式小结(推荐)
2016/11/09 Javascript
ES6中的rest参数与扩展运算符详解
2017/07/18 Javascript
vue引入新版 vue-awesome-swiper插件填坑问题
2018/01/25 Javascript
js中Object.defineProperty()方法的不详解
2018/07/09 Javascript
微信小程序支付PHP代码
2018/08/23 Javascript
JavaScript中的事件与异常捕获详析
2019/02/24 Javascript
详解Vue-Router源码分析路由实现原理
2019/05/15 Javascript
浅谈ECMAScript 中的Array类型
2019/06/10 Javascript
Vue 微信端扫描二维码苹果端却只能保存图片问题(解决方法)
2020/01/19 Javascript
[56:35]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第一局
2016/03/06 DOTA
[01:18:21]EG vs TNC Supermajor小组赛B组败者组第一轮 BO3 第一场 6.2
2018/06/03 DOTA
python实现读取并显示图片的两种方法
2017/01/13 Python
DataFrame中去除指定列为空的行方法
2018/04/08 Python
浅析Python装饰器以及装饰器模式
2018/05/28 Python
基于numpy中数组元素的切片复制方法
2018/11/15 Python
python3实现将json对象存入Redis以及数据的导入导出
2020/07/16 Python
Python爬虫之Selenium中frame/iframe表单嵌套页面
2020/12/04 Python
使用CSS3的rem属性制作响应式页面布局的要点解析
2016/05/24 HTML / CSS
学习十八届三中全会精神实施方案
2014/02/17 职场文书
年终考核实施方案
2014/05/26 职场文书
内勤岗位职责
2015/02/10 职场文书
道歉信范文
2015/05/12 职场文书
对讲机知识
2022/04/07 无线电
TypeScript实用技巧 Nominal Typing名义类型详解
2022/09/23 Javascript