Table of Contents
element-ui 版本:2.13.2
对于页码(current-page)和每页条数(page-size)的改变,el-pagination 分别会触发 size-change 和 current-change 事件。为了方便同步数据,current-page 和 page-size 都使用了 sync 修饰符。
这就导致了一个问题:如果修改每页条数使得当前页码超出范围,el-pagination 会在触发 size-change 事件后再触发 current-change 事件。如果没有任何防抖节流措施,会看到网络面板里有两个请求,并且第一个请求的参数页码可能是不对的。这里说可能,是因为对于参数的处理会导致结果不同。
首先,对于请求,我一般会用一个变量标记是否正在进行,如果是,禁止再次请求。
getData() { if (this.loading) return this.loading = true request({}) // ………… }
这样处理后就避免了两次请求的情况。但是,这也导致了页面空白的问题。
解构导致绑定值无法及时更新
vue 是双向绑定的,sync 修饰符可以让子组件改变父组件的值。当改变每页条数导致页码改变的时候,只有 current-change 触发的时候页码才会是正确的值。但我代码里只会发出 size-change 的请求。如果请求时我对查询对象进行解构处理,即实际传给接口的值脱离了 vue 的响应式系统,就会导致页面空白问题。为此,可以将解构、请求等处理放到 nextTick 里,确保 current-page 变化之后再获取数据。
// 获取列表 fetchData() { if (this.listLoading) return this.listLoading = true this.$nextTick(() => { const tempQuery = { ...this.query } // 一些处理 getInvoiceRecordList(tempQuery) .then(response => { this.list = response.data ?? [] this.total = response.page?.totalItems ?? 0 }) .finally(() => { this.listLoading = false }) }) },
虽然这解决了问题,但还有一个疑问:为什么没有解构的参数就可以不使用 nextTick 而获取到正确的值呢?
Axios 的请求不是立即发出的
在不对 this.query 解构的情况下,虽然第一次执行 fetchData 时请求参数不正确,但是最终还是拿到了正确的数据,实际参数也是正确的。这说明在 axios 发出请求之前,current-change 就已经完成了页码的修改。难道 axios 的请求不是立即发出的?来,看下 axios 的代码:
// axios/lib/core/Axios.js /** * Dispatch a request * * @param {Object} config The config specific for this request (merged with this.defaults) */ Axios.prototype.request = function request(config) { /*eslint no-param-reassign:0*/ // Allow for axios('example/url'[, config]) a la fetch API if (typeof config === 'string') { config = utils.merge({ url: arguments[0] }, arguments[1]); } config = utils.merge(defaults, {method: 'get'}, this.defaults, config); config.method = config.method.toLowerCase(); // Hook up interceptors middleware var chain = [dispatchRequest, undefined]; var promise = Promise.resolve(config); this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); }); while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; };
axios 把拦截器配置和请求放到了 promise.then 里执行。而 promise.then 是微任务。当真正发起请求的时候,vue 已经把 current-page 同步为正确的值了。