上一步的实现效果乍一看好像没什么不对劲的地方,但是如果直接改变地址栏的话,显示的当前页和当前数据都不会变化。前端路由在页面的查询参数(指的是 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