el-tabs频繁切换tab引发的数据渲染混淆

el-tabs 遇上"切换焦虑":数据渲染的"小混乱"

el-tabs让用户能轻松地来回切换,可以在一个页面显示更多的内容,方便用户查看自己想要的内容。 但是当我们觉得一切很顺利是,却忽然发现它也会闹出一些小情绪,引发数据渲染的"小混乱"。今天,咱们就来聊聊这个有趣又让人头疼的话题。

如果每个标签页的内容组件都是一些简单的静态内容,或者是一次请求所有标签的内容,那倒不会出什么问题。可一旦这些组件里涉及到异步数据请求、复杂的状态管理,事情就变得有趣起来了。

数据渲染的"小混乱"是如何诞生的?

异步请求的"撞车"事件

想象一下,你正在看标签页 A 的内容,这个页面需要从服务器请求一些数据来展示。你耐心地等着数据加载完成,可就在这个时候,你突然心血来潮,切换到了标签页 B。标签页 B 一看,嘿,我这儿也需要从服务器拿数据,于是也赶紧发起了请求。

等一会儿,标签页 B 的数据先回来了,它兴冲冲地更新了自己的内容。可没过多久,标签页 A 的数据也回来了,它也不管你现在看的是标签页 B,直接就把自己的内容更新了。这就导致了一个很尴尬的情况:你明明在看标签页 B,可它的内容却被标签页 A 的数据给覆盖了。这就像是两辆车在路口"撞车"了,谁也不让谁,结果大家都乱了套。

一个直观的示例

为了更直观地理解这个问题,我们来看一个简单的示例代码。在这个例子中,每个标签页的内容组件都会发起一个异步请求来加载数据。我们使用 setTimeout 来模拟异步请求,每个标签页的延迟时间不同,以便更好地理解延迟高的数据覆盖延迟低的数据的场景。

为了更直观的理解,贴上造成这场"小混乱"的代码。

在点击 tab 时实时请求数据,使用 setTimeout 来模拟异步请求,每个 tab 的延迟时间设置为100ms、1000ms、500ms、2000ms,这样可以更好的理解延迟高的数据覆盖延迟低的数据的场景,妥妥的"后来居上"。

typescript 复制代码
<script setup lang="ts">
  import type { TabsPaneContext } from "element-plus";
  import { ref,onMounted } from "vue";

  const tabs = [
    { label: "Tab 1", name: "1" },
    { label: "Tab 2", name: "2" },
    { label: "Tab 3", name: "3" },
    { label: "Tab 4", name: "4" },
  ];
  const activeTab = ref("1");

  const data = ref("");

  const fetchData = async () => {
    if (activeTab.value === "1")
      return new Promise((resolve) =>
        setTimeout(() => resolve("Content 1"), 100)
                        );
    if (activeTab.value === "2")
      return new Promise((resolve) =>
        setTimeout(() => resolve("Content 2"), 1000)
                        );
    if (activeTab.value === "3")
      return new Promise((resolve) =>
        setTimeout(() => resolve("Content 3"), 500)
                        );
    if (activeTab.value === "4")
      return new Promise((resolve) =>
        setTimeout(() => resolve("Content 4"), 2000)
                        );
  };

  const setActiveTab = async (tab: TabsPaneContext) => {
    activeTab.value = tab.paneName as string;
    data.value = await fetchData() as string;
  };

  onMounted(() => {
    setActiveTab({ paneName: activeTab.value });
  }); 

</script>

<template>
  <div>
    <el-tabs v-model="activeTab" @tab-click="setActiveTab">
      <el-tab-pane
        v-for="(tab, index) in tabs"
        :key="index"
        :label="tab.label"
        :name="tab.name"
        >
        <div>{{ data }}</div>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

如何避免这场"小混乱"?

1. 独立状态管理

每个标签页的内容组件应独立管理自己的状态,避免依赖全局状态。这种方法类似于为每个组件分配一个独立的"数据容器",确保它们互不干扰。通过这种方式,即使一个标签页修改了自己的状态,也不会影响到其他标签页。这类似于给每个用户分配一个独立的"物品",避免了因共享资源导致的冲突。

2. 设置标志符,确保只使用对应请求数据

通过设置一个标识符(如 currentTab),在切换标签页时取消之前的请求,只使用本次点击的标签页发出的请求的数据来渲染 tabPane。这种方法可以确保数据的正确性和一致性,避免因请求"撞车"导致的数据混乱。

3. 取消未完成的请求

通过 axiosCancelToken 或其他方式取消未完成的请求,只保留当前点击的请求获取的数据。这种方法可以确保不会因未完成的请求干扰当前标签页的数据渲染。

4. 使用独立组件管理每个标签页

将每个标签页的内容封装为独立组件,每个组件管理自己的状态和数据请求。这种方法可以有效避免因共享状态导致的数据混淆,确保每个标签页的数据独立性。

5. 合理管理异步请求

使用像 TanStack Query 这样的库来管理异步请求。这些库提供了强大的请求管理功能,可以有效避免请求"撞车"问题。它们会自动处理请求的状态,并确保数据正确地更新到对应的组件中。

... 更多解决方案,不再赘述。

重点说说------使用TanStack Query来管理异步请求

上述几种避免混乱的方案,取消请求的实现稍微复杂一些,其次对大家来说比较陌生的就是TanStack Query,实际上使用它来处理 el-tabs 数据混淆问题,非常简单,在此着重说一说。

以下是使用TanStack Query优化后的代码

xml 复制代码
<script setup lang="ts">
import { useQuery } from "@tanstack/vue-query";
import type { TabsPaneContext } from "element-plus";
import { ref } from "vue";

const tabs = [
  { label: "Tab 1", name: "1" },
  { label: "Tab 2", name: "2" },
  { label: "Tab 3", name: "3" },
  { label: "Tab 4", name: "4" },
];
const activeTab = ref("1");


const fetchData = async () => {
  if (activeTab.value === "1")
    return new Promise((resolve) =>
      setTimeout(() => resolve("Content 1"), 100)
    );
  if (activeTab.value === "2")
    return new Promise((resolve) =>
      setTimeout(() => resolve("Content 2"), 1000)
    );
  if (activeTab.value === "3")
    return new Promise((resolve) =>
      setTimeout(() => resolve("Content 3"), 500)
    );
  if (activeTab.value === "4")
    return new Promise((resolve) =>
      setTimeout(() => resolve("Content 4"), 2000)
    );
};

const setActiveTab = async (tab: TabsPaneContext) => {
  activeTab.value = tab.paneName as string;
  await refetch();
};


const { data,isFetching, refetch, isError } = useQuery({
  queryKey: ["tabContent", activeTab.value],
  queryFn: () => fetchData(),
  enabled: activeTab.value==='1',
 
});
</script>

<template>
  <div>
    <el-tabs v-model="activeTab" @tab-click="setActiveTab">
      <el-tab-pane
        v-for="(tab, index) in tabs"
        :key="index"
        :label="tab.label"
        :name="tab.name"
      >
        <div v-if="isFetching">Loading...</div>
        <div v-else-if="isError">Error occurred</div>
        <div v-else>{{ data }}</div>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

现在无论如何点击切换 4 个 tab,数据也是齐齐整整一丝不苟的渲染到对应的 tabPane 了。

为什么 TanStack Query 能有效避免数据混淆

  • 独立缓存:每个标签页的数据请求都有独立的缓存,即使切换标签页也不会互相干扰。
  • 后台更新:即使用户切换标签页,后台请求仍然会完成,并更新缓存,确保数据的一致性。
  • 状态同步:所有使用相同查询键的组件都会自动同步到最新数据,避免了因状态不一致导致的混乱。

TanStack Query 作为一个非常强大的库,不仅可以解决异步请求的"撞车"问题,还可以提供更多的功能,如数据缓存、错误处理等,值得我们在实际项目中深入挖掘和应用。

总结

在实际开发中,类似el-tabs 数据渲染混乱的问题并不少见,尤其是在涉及异步数据请求和复杂状态管理时,比如进行数据筛选时,如果请求返回时间长,也容易导致数据的渲染错误。通过使用缓存机制、独立状态管理、合理管理异步请求、取消未完成的请求以及封装独立组件等方法,可以有效避免这些问题,提升用户体验。

希望这篇文章能帮助你更好地理解和解决 el-tabs 数据渲染混乱的问题。如果你在开发中遇到类似的问题,不妨尝试上述方法,相信会有所帮助。

欢迎交流开发过程中遇到的各种问题,让我们一起探索前端开发的更多可能性!

有任何问题或者兴趣也可以在公众号:自由前端之路 找到我。

相关推荐
崔庆才丨静觅11 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606112 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了12 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅12 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅13 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅13 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment13 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅13 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊13 小时前
jwt介绍
前端
爱敲代码的小鱼14 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax