代码分割与 keep-alive
约 717 字大约 2 分钟
布欧-Lewyon
2026-05-15
首页 › Vue › 测试与性能 › 代码分割与 keep-alive
代码分割(Code Splitting)
路由级别分割
最常用的分割方式——每个页面一个独立的 chunk:
const routes = [
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue')
},
{
path: '/admin',
component: () => import('@/views/Admin.vue')
}
]构建后每个页面产出一个独立的 JS 文件,访问时才加载。
组件级别分割
<script setup>
import { defineAsyncComponent } from 'vue'
const HeavyChart = defineAsyncComponent(() =>
import('@/components/HeavyChart.vue')
)
</script>
<template>
<Suspense>
<HeavyChart />
<template #fallback>
<div class="chart-skeleton">图表加载中...</div>
</template>
</Suspense>
</template>defineAsyncComponent 选项
<script setup>
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('@/components/Heavy.vue'),
loadingComponent: LoadingSpinner, // 加载中
delay: 200, // 延迟显示 loading(避免闪烁)
timeout: 3000, // 超时时间
errorComponent: ErrorDisplay, // 加载失败
onError(retry, fail, attempts) {
if (attempts < 3) retry() // 自动重试 3 次
else fail()
}
})
</script>条件加载
只有当条件满足时才加载(不预加载):
<script setup>
import { defineAsyncComponent, ref } from 'vue'
const showChart = ref(false)
// 组件只在 showChart=true 时才被加载
const Chart = defineAsyncComponent(() =>
import('@/components/Chart.vue')
)
function loadChart() {
showChart.value = true
}
</script>
<template>
<button @click="loadChart">加载图表</button>
<Chart v-if="showChart" />
</template><KeepAlive>
基础用法
<KeepAlive> 缓存组件实例,避免反复销毁重建:
<template>
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
</template>条件缓存
<!-- 只缓存 TabA 和 TabB -->
<KeepAlive :include="['TabA', 'TabB']">
<component :is="currentTab" />
</KeepAlive>
<!-- 排除 TabC -->
<KeepAlive :exclude="['TabC']">
<component :is="currentTab" />
</KeepAlive>
<!-- 最多缓存 5 个实例 -->
<KeepAlive :max="5">
<component :is="currentTab" />
</KeepAlive>
include/exclude匹配的是组件的name选项(Options API)或文件名(<script setup>自动根据文件名推断)。
KeepAlive 的生命周期
<script setup>
import { onActivated, onDeactivated } from 'vue'
// 组件被 KeepAlive 激活时
onActivated(() => {
console.log('组件从缓存中激活')
refreshData() // 恢复时刷新数据
})
// 组件被 KeepAlive 停用时
onDeactivated(() => {
console.log('组件被缓存起来')
saveScrollPosition() // 保存滚动位置
})
</script>缓存 Tab 切换实战
<!-- TabsContainer.vue -->
<script setup>
import { shallowRef } from 'vue'
import TabA from './TabA.vue'
import TabB from './TabB.vue'
import TabC from './TabC.vue'
const tabs = {
a: { component: TabA, title: '概览' },
b: { component: TabB, title: '详情' },
c: { component: TabC, title: '设置' }
}
const active = ref('a')
</script>
<template>
<div class="tabs">
<button v-for="(tab, key) in tabs" :key="key"
:class="{ active: key === active }"
@click="active = key">
{{ tab.title }}
</button>
<!-- 缓存所有 tab,最多 3 个 -->
<KeepAlive :max="3">
<component :is="tabs[active].component" />
</KeepAlive>
</div>
</template>路由级 KeepAlive
<!-- App.vue -->
<template>
<router-view v-slot="{ Component, route }">
<KeepAlive :include="cachedRoutes">
<component :is="Component" :key="route.name" />
</KeepAlive>
</router-view>
</template>
<script setup>
import { computed } from 'vue'
// 根据路由 meta 决定哪些页面需要缓存
const cachedRoutes = computed(() => {
return route.meta?.keepAlive ? [route.name] : []
})
</script>路由配置:
{
path: '/list',
component: () => import('@/views/List.vue'),
meta: { keepAlive: true } // 列表页缓存,回到时保持滚动位置
}小结
| 技术 | 用途 |
|---|---|
() => import() | 路由/组件级代码分割 |
defineAsyncComponent | 组件懒加载,支持 loading/error 状态 |
<KeepAlive> | 缓存组件实例,避免重建 |
include / exclude | 选择性缓存 |
onActivated / onDeactivated | 缓存激活/停用时的生命周期 |
上一节:Vitest + Vue Test Utils 下一节:虚拟列表与响应式优化
