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 数据渲染混乱的问题。如果你在开发中遇到类似的问题,不妨尝试上述方法,相信会有所帮助。

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

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

相关推荐
刺客-Andy7 分钟前
React 第四十一节Router 中 useActionData 使用方法案例以及注意事项
前端·react.js·前端框架
岁岁岁平安11 分钟前
Vue3学习(组合式API——reactive()和ref()函数详解)
前端·javascript·vue.js·学习·vue3·reactive·ref
肠胃炎13 分钟前
React事件机制
前端·javascript·react.js
CUIYD_198921 分钟前
javascript —— ! 和 !! 的区别与作用
前端·javascript·vue.js
慌糖1 小时前
vue3搭建脚手架前的前置知识
vue.js
帅帅哥的兜兜2 小时前
next.js实现项目搭建
前端·react.js·next.js
筱歌儿2 小时前
css 左右布局
前端·css
GISer_Jing2 小时前
编译原理AST&以Babel为例进行解读、Webpack中自定义loader与plugin
前端·webpack·node.js
GISer_Jing2 小时前
Webpack中Compiler详解以及自定义loader和plugin详解
前端·webpack·node.js
浩~~2 小时前
CSS常用选择器
前端·css