概述
在具有多级Tab切换功能的应用中,当用户切换Tab时需要正确处理数据的生命周期。本文档详细描述如何实现watch(() => tabStore.activeSubTab, ...)
这一关键模式,该模式负责在Tab切换时重置分页状态、清空旧数据并触发新数据加载。
核心实现
watch(
() => tabStore.activeSubTab, // 监听当前活动Tab的变化
(newTab) => {
// 1. 重置分页状态
page.value = 1;
// 2. 清空旧数据
dataList.value = [];
// 3. 触发新数据加载
loadMoreData();
}
);
功能分解
1. 状态重置的必要性
状态 | 重置原因 | 不重置的后果 |
---|---|---|
分页(page) | 每个Tab应从第一页开始加载 | 新Tab会延续旧Tab的页码,导致数据错乱 |
数据列表 | 避免新旧Tab数据混杂 | 用户看到不相关的混合数据 |
加载状态 | 中断旧Tab的加载,准备新Tab加载 | 请求冲突,可能导致UI状态异常 |
2. 实现细节优化
2.1 添加防抖处理(避免快速切换问题)
import { debounce } from 'lodash-es';
// 创建带防抖的加载函数
const debouncedLoad = debounce(() => {
loadMoreData();
}, 300);
watch(
() => tabStore.activeSubTab,
(newTab) => {
page.value = 1;
dataList.value = [];
// 使用防抖版本
debouncedLoad();
// 取消可能的待处理请求
debouncedLoad.cancel();
}
);
2.2 请求取消机制(防止竞态条件)
let abortController = null;
const loadMoreData = async () => {
// 取消前一个请求
abortController?.abort();
abortController = new AbortController();
try {
const response = await fetch(`/api/data?tab=${tabStore.activeSubTab}`, {
signal: abortController.signal
});
// 处理响应...
} catch (err) {
if (err.name !== 'AbortError') {
// 处理真实错误
}
}
};
watch(() => tabStore.activeSubTab, () => {
// 切换时取消所有待处理请求
abortController?.abort();
// ...其他重置逻辑
});
3. 生命周期管理
组件挂载时
onMounted(() => {
// 初始加载当前Tab数据
loadMoreData();
});
组件卸载时
onUnmounted(() => {
// 1. 取消所有待处理请求
abortController?.abort();
// 2. 清理防抖函数
debouncedLoad.cancel();
// 3. 重置组件状态
page.value = 1;
dataList.value = [];
});
性能优化策略
1. 数据缓存实现
// 在Pinia store中添加缓存
const tabDataCache = reactive({});
watch(
() => tabStore.activeSubTab,
(newTab) => {
// 检查缓存
if (tabDataCache[newTab]) {
dataList.value = tabDataCache[newTab].data;
page.value = tabDataCache[newTab].page;
} else {
// 无缓存则重置加载
resetAndLoad();
}
}
);
// 在加载函数中更新缓存
const loadMoreData = async () => {
// ...加载数据
tabDataCache[tabStore.activeSubTab] = {
data: dataList.value,
page: page.value
};
};
2. 请求合并技术
let currentRequestId = 0;
const loadMoreData = async () => {
const requestId = ++currentRequestId;
// ...加载数据
// 检查是否为最新请求
if (requestId !== currentRequestId) {
console.log('过时请求被忽略');
return;
}
// 处理响应...
};
错误处理与边界情况
1. 处理网络异常
const loadMoreData = async () => {
try {
// ...请求逻辑
} catch (error) {
if (error.name === 'AbortError') {
console.log('请求被取消');
} else {
// 显示错误提示
showErrorToast(`加载失败: ${error.message}`);
// 重试机制
if (retryCount < 3) {
setTimeout(() => loadMoreData(), 2000);
retryCount++;
}
}
}
};
2. 空状态处理
<template>
<div v-if="dataList.length === 0 && !isLoading">
<EmptyState :tab="tabStore.activeSubTab" />
</div>
</template>
完整示例代码
<script setup>
import { ref, watch, onMounted, onUnmounted } from 'vue';
import { useTabStore } from '@/stores/tabStore';
import { debounce } from 'lodash-es';
const tabStore = useTabStore();
const page = ref(1);
const dataList = ref([]);
const isLoading = ref(false);
let abortController = null;
let currentRequestId = 0;
// 带防抖的加载函数
const debouncedLoad = debounce(loadMoreData, 300);
// Tab切换监听
watch(
() => tabStore.activeSubTab,
(newTab) => {
// 取消待处理操作
abortController?.abort();
debouncedLoad.cancel();
// 重置状态
page.value = 1;
dataList.value = [];
currentRequestId++;
// 触发新加载
debouncedLoad();
}
);
// 数据加载函数
async function loadMoreData() {
if (isLoading.value) return;
try {
isLoading.value = true;
const requestId = ++currentRequestId;
// 取消前一个请求
abortController?.abort();
abortController = new AbortController();
const response = await fetch(
`/api/data?tab=${tabStore.activeSubTab}&page=${page.value}`,
{ signal: abortController.signal }
);
// 检查是否为最新请求
if (requestId !== currentRequestId) return;
const newData = await response.json();
// 检查Tab是否已切换
if (tabStore.activeSubTab !== newData.tab) {
console.warn('数据与当前Tab不匹配');
return;
}
dataList.value = [...dataList.value, ...newData.items];
page.value++;
} catch (err) {
if (err.name !== 'AbortError') {
console.error('加载失败', err);
}
} finally {
isLoading.value = false;
}
}
onMounted(() => {
// 初始加载
loadMoreData();
});
onUnmounted(() => {
// 清理资源
abortController?.abort();
debouncedLoad.cancel();
});
</script>
最佳实践总结
- 状态隔离:每个Tab应维护独立的分页和数据状态
- 资源清理:及时取消请求和清理副作用
- 防抖优化:防止快速切换导致的性能问题
- 缓存策略:提升频繁切换场景的用户体验
- 错误边界:优雅处理网络异常和边界情况
- 状态校验:关键操作前验证当前状态有效性
- UI反馈:提供清晰的加载状态和空状态提示