问题描述:
当使用 Vant 的 PullRefresh 组件下拉刷新时,如果在请求接口过程中点击 Popup 弹窗,弹窗会等到接口结束后才显示
问题原因
- JavaScript 是单线程的,网络请求会阻塞 UI 更新
- Vant 的 PullRefresh 的 loading 状态优先级较高
- Popup 的显示被浏览器主线程的繁忙任务延迟
解决方案
方案1:使用 setTimeout 强制弹窗立即显示
javascript
methods: {
showPopup() {
// 使用 setTimeout 让弹窗显示任务进入下一个事件循环
setTimeout(() => {
this.popupVisible = true;
}, 0);
}
}
方案2:分离数据加载和 UI 更新
javascript
async onRefresh() {
this.$refs.pullRefresh.toggleLoading(true);
try {
// 将数据请求放在 Promise 中
const dataPromise = this.fetchData();
// 立即更新 UI 状态
this.$nextTick(() => {
this.popupVisible = true;
});
await dataPromise;
} finally {
this.$refs.pullRefresh.toggleLoading(false);
}
}
方案3:使用 Web Worker 处理数据请求(适用于复杂场景)
ini
// 创建 worker.js
const worker = new Worker('./worker.js');
methods: {
onRefresh() {
this.$refs.pullRefresh.toggleLoading(true);
worker.postMessage({ type: 'fetchData' });
worker.onmessage = (e) => {
if (e.data.type === 'dataFetched') {
this.data = e.data.payload;
this.$refs.pullRefresh.toggleLoading(false);
}
};
// 弹窗可以立即显示
this.popupVisible = true;
}
}
方案4:优化请求性能(推荐)
javascript
methods: {
async onRefresh() {
// 1. 先显示弹窗
this.popupVisible = true;
// 2. 开始刷新
this.$refs.pullRefresh.toggleLoading(true);
try {
// 3. 使用 Promise.race 确保请求不会阻塞太久
await Promise.race([
this.fetchData(),
new Promise(resolve => setTimeout(resolve, 500)) // 超时保护
]);
} finally {
this.$refs.pullRefresh.toggleLoading(false);
}
},
fetchData() {
return new Promise((resolve) => {
// 你的数据请求逻辑
setTimeout(() => {
this.list = [...]; // 更新数据
resolve();
}, 1000);
});
}
}
最佳实践方案
关键点说明
- 使用
requestAnimationFrame
或setTimeout
:将弹窗显示操作放入下一个事件循环 - 分离数据加载和UI更新:不要让数据请求阻塞UI操作
- 合理使用
$nextTick
:确保Vue的DOM更新完成 - 考虑请求超时:避免长时间请求完全阻塞UI