WSの小屋

利用 v-if 与 v-show 实现懒加载 Tab 页切换

在 Vue 中实现选项卡(Tab)切换时,如果每个选项卡内容都包含复杂组件,如何平衡性能用户体验就成为一个需要考虑的问题。

问题分析

先了解 Vue 的两个内置指令:

v-if 的特性

  • 真正的条件渲染:条件为假时,元素根本不会被渲染
  • 触发完整生命周期:每次切换都会触发组件挂载(mount)和销毁(unmount)
  • 能节省初始化内存:未显示的内容不会占用内存

v-show 的特性

  • 仅切换 display 属性:元素始终保留在 DOM 中
  • 不触发生命周期:切换时不会重新挂载
  • 能保持组件状态:表单输入、滚动位置等不会重置

两种方式的缺陷

方式 问题
只用 v-if 每次切换都重新挂载,状态丢失,请求重复发送
只用 v-show 所有 Tab 同时挂载,初始加载开销大

理想方案:默认只挂载第一个 Tab,切换到某个 Tab 时才挂载它,且之后不再注销——即懒加载 + 缓存状态

实现方案

第一种:懒加载容器组件

创建一个通用组件 LazyView.vue

<script setup lang="ts">
const { active = "default" } = defineProps<{
  active?: string;
}>();

const set = reactive(new Set<string>());

watch(
  () => active,
  (val) => set.add(val),
  { immediate: true }
);
</script>

<template>
  <div v-for="item in Object.keys($slots)" v-show="item == active">
    <slot v-if="set.has(item)" :name="item" />
  </div>
</template>

使用方式:

<lazy-view :active="activeTab">
  <template #page1>
    <Page1 />
  </template>
  <template #page2>
    <Page2 />
  </template>
  <template #page3>
    <Page3 />
  </template>
</lazy-view>

优点:通用性强,哪个 Tab 都可以用。

第二种:使用 keep-alive

Vue 内置的 <KeepAlive> 组件也可以实现类似效果:

<KeepAlive>
  <component :is="currentComponent" v-if="loadedComponents.includes(currentTab)" />
</KeepAlive>

<script setup>
const loadedComponents = reactive(new Set())

watch(() => currentTab, (tab) => {
  loadedComponents.add(tab)
}, { immediate: true })
</script>

与第一种的区别:

方案 代码复杂度 灵活性 缓存机制
LazyView 手动控制
KeepAlive include/exclude 控制

第三种:动态组件 + 已加载集合

<script setup lang="ts">
const tabs = ['page1', 'page2', 'page3']
const active = ref('page1')
const loaded = ref(new Set(['page1']))  // 默认加载第一个

function switchTab(tab: string) {
  loaded.value.add(tab)
  active.value = tab
}
</script>

<template>
  <div v-for="tab in tabs" v-show="tab === active" :key="tab">
    <component :is="tabComponentMap[tab]" v-if="loaded.has(tab)" />
  </div>
</template>

性能对比

指标 v-if v-show LazyView KeepAlive
初始渲染 ✅ 仅当前 ❌ 全部 ✅ 仅当前 ✅ 仅当前
切换开销 高(重挂载) 中(首次挂载)
状态保持 ❌ 丢失 ✅ 保持 ✅ 保持 ✅ 保持
内存占用

实际应用场景

  • 管理系统 Tab 页:多个数据列表页之间切换,避免重复请求
  • 表单分步填写:多步骤表单,切换到下一步时保留上一步输入
  • 仪表板面板:多个图表面板懒加载,减少首页加载压力

总结

利用 v-if 和 v-show 各自的特性,再结合一个状态集合来追踪已加载的 Tab,可以实现按需加载 + 状态缓存的理想效果。这种方式既避免了首次加载所有内容的性能浪费,也解决了切换时状态丢失的问题,是 Tab 页面交互的最佳实践。

Comments | 0条评论

  • 头像回复发布于 2026/06/24 20:30:00

    这个 LazyView 组件很实用,直接拷走用了 👍