最近比较空闲,公司的后台就想着把如今的后台管理系统给改版一下,说是之前的太难看了,用着也很差用,而后给我甩过来一个ant-design-pro的连接,说是他看这个就挺不错的。css
我当时内心就想着,以前的那个项目混合在大家的java项目里,跟普通的jsp页面差很少,一下就是一大堆的css和js文件,看着我都惧怕(好吧,我认可其实我都不敢看),这能加载的快了就奇了怪了。ant-design最初是为react设计的,ant-design-pro天然也是用react了,不得不说人家这个界面看着确实舒服。前端
对着ant-design-pro的官方文档看了一通,貌似看了跟没看也差很少???算了,仍是直接看代码吧,整理了一下思路,大体上是看懂了,除了react + react-router外,状态管理用的是 dva, redux的异步问题算是解决了,要不就开始直接写页面吧?vue
等等,我好像漏掉了点什么?噢,对,先看看打包出来的文件大小,一打包个人心就凉了,最大的js竟然有900多k,ant-design的源文件是真的大。react我还只是能写出代码,打包优化这个可就有点为难我了。这时的我再想到公司那1m的带宽,还有这几个后台的技术能力,要否则这个技术栈我仍是放弃吧?不能期望连 请求头, CORS稍微高级一点的携带cookie, nginx静态服务器 都搞不懂的人去给我弄个静态服务器,再顺便开启一下gzip吧?算了算了,找找有没有vue + element-ui的后台模板,不用太费劲就找到了vue-element-admin。java
vue-element-admin用着还行,就是界面不太符合个人理想状况,就对着ant-design-pro改造了一点,列表页大概就是下面这样了。列表的数据是要分页的,普通的列表页只有一个页面栈,也就是用户点击地址栏的回退地址栏时,会返回上一个页面栈,而不是上一页的数据,不太符合用户习惯吧?毕竟传统的网站都是能够回退到上一页的,嗯,话很少说,进入正题吧。react
假设列表页的路径是 /user/list,分页相关的参数为{ page: 1, pagesize: 10 }
,从其余页面跳转过来的时候,咱们的路径一般是不包含任何参数的,以后的列表数据都是根据该页面的page和pagesize进行变化的,当未使用keep-alive缓存组件时,每次进入列表页都至关于第一次进入,也就是说每次都只能获取第一页的数据。nginx
既然列表数据是用page和pagesize进行变化的,那直接从地址栏获取page和pagesize进行赋值不就行了?那么是改变地址栏的代码是直接写在当前页面仍是 独立为分页组件 呢?从复用性方面来讲,仍是独立出来的好,毕竟其余页面可能也会使用到,总不能每次都复制粘贴吧,那组件化的意义何在?固然了,也不是说分页就必须用这个自定义的分页组件,只推荐在主页面(非遮罩层,有的页面会在点击某一行数据时出现遮罩层显示子列表,此时使用element-ui的分页组件便可)须要分页时使用。git
当改变地址栏的时候,咱们是不但愿不带分页参数的页面栈存在的,此时用replace直接替换便可。github
MyPagination.vue的初始结构为:element-ui
<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>
复制代码
父组件的关键代码:redux
<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);
}
}
},
复制代码
在上一步的效果中,当改变地址栏的page和pagesize时,列表页的数据也会随之变化。既然是根据地址栏的参数变化,那么新的问题就产生了,
这个时候主要就看后台怎么设计了,
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中的后续操做,取消本次请求。替换页面之后,请求远程数据,更新当前页和数据的显示。
若是是首次进入页面的话,totalPages第一次是0,也就是地址栏的参数将不会发生变化,这时候就会出现地址栏和分页组件的显示不一致的状况。这时候能够在分页组件中watch totalPages的变化。
totalPages(newVal, oldVal) {
if (+oldVal === 0 && newVal > 0) {
this.handlePage('replace', this.page, +this.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
若是有什么问题,欢迎你们留言进行探讨。