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

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

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

相关推荐
itwlz5 分钟前
vite配置@别名,以及如何让IDE智能提示路经
开发语言·前端·javascript
lichenyang4537 分钟前
添加按钮跳转页面并且根据网站的用户状态判断是否显示按钮
开发语言·前端·javascript
皮皮高8 分钟前
itvbox绿豆影视tvbox手机版影视APP源码分享搭建教程
android·前端·后端·开源·tv
白云~️23 分钟前
table表格合并,循环渲染样式
javascript·vue.js·elementui
Hilaku25 分钟前
JavaScript 里的 !0、!1 到底是啥?聊聊那些压缩器最爱的“极简写法”
前端·javascript
全栈陈序员34 分钟前
前端文件下载常用方式详解
前端·javascript·chrome·ajax·css3·html5·safari
二十一_43 分钟前
🤖✨ ChatGPT API深度体验:让AI看懂图片、听懂语音、调用你的代码
前端·chatgpt·openai
Developer_Niuge1 小时前
前端批量请求失败重复弹窗的正确解决方案
前端
前端小饭桌1 小时前
告别嵌套地狱:用数据结构优化解决 JS 多层循环的混乱与静默错误
前端·javascript
爱摸鱼的格子1 小时前
🚀 你真的会用 Promise.all 吗?10 个实用技巧助你成为异步处理大师!
前端