keep-alive:我不死,只是沉睡;onActivated:我醒了!

在 Vue 的开发过程中,页面切换、组件复用是一种非常常见的创建。为了避免每次切换都重新销毁组件从而浪费性能,Vue 提供了一个神奇的内置组件:<keep-alive>。

而为了配合它对组件进行“缓存式挂起和激活”的能力,Vue 提供了两个关键的生命周期钩子函数:onActivated 和 onDeactivated。

这两个组合功能,既是性能优化的利器,也是组件状态管理的核心部分。

1. 什么是 <keep-alive>?

1.1 基本介绍

<keep-alive> 是 Vue 提供的内置组件,用来缓存不活动的组件实例,而不是销毁它们。

当在页面中切换组件时,如果不使用 keep-alive,Vue 会把旧组件销毁,再创建新组件。而加上 keep-alive 后,旧组件会被缓存起来而不是销毁,下次再切换回来的时候会直接激活而不是重新创建

1.2 基本用法
<template>
  <keep-alive>
    <component :is="currentView" />
  </keep-alive>
</template>

<script setup>
import { ref } from 'vue'

const currentView = ref('Home')
</script>

currentView 控制当前显示哪个组件,<keep-alive> 会缓存之前展示过的组件实例。

1.3 额外用法

1、include / exclude 筛选组件缓存

<keep-alive include="Home,About">
  <router-view />
</keep-alive>

也可以使用正则表达式: 

<keep-alive :include="/^Home|About$/">

还可以动态绑定:

<keep-alive :include="cachedComponents">
  <router-view />
</keep-alive>

<script setup>
const cachedComponents = ref(['Home', 'About'])
</script>

2、max:控制最多缓存多少个组件

<keep-alive :max="3">

当使用 max 属性时,Vue 内部会使用一个 LRU 算法,维持缓存组件的使用顺序:

  • 每次激活一个组件,放到链表头
  • 超出缓存数量,移除链表尾的组件(最久没使用的)

2.  keep-alive 是如何工作的?

2.1 缓存机制

当一个组件被 <keep-alive> 包裹,它第一次渲染会被正常挂载,但是在切换离开时,它不会被销毁,而是会进入“缓存池”,等到再次被显示时,会从缓存中取出这个组件实例,而不是重新创建。

2.2 生命周期的变化

默认组件的生命周期:

beforeCreate -> created -> beforeMount -> mounted -> beforeUnmount -> unmounted

但被 keep-alive 包裹的组件,在第一次渲染后,会有不同的行为:

beforeCreate -> created -> beforeMount -> mounted
切换出去:
 -> deactivated
切换回来:
 -> activated

也就是说,组件并没有被销毁,只是被“挂起”了!

3. onActivated 钩子函数

3.1 介绍

onActivated 是 Vue 3 中的 Composition API 生命周期钩子函数,用于监听被 <keep-alive> 缓存的组件重新激活时的行为。

3.2 使用场景
  • 页面切换回来后需要重新发请求
  • 需要手动刷新 DOM 或重新获取状态
  • 需要恢复滚动位置
  • 实时数据重新拉取

举个 🌰

<!-- App.vue -->
<template>
  <keep-alive>
    <UserInfo v-if="activeTab === 'user'" />
  </keep-alive>

  <Settings v-if="activeTab === 'settings'" />
</template>

<script setup>
import { ref } from 'vue'
import UserInfo from './UserInfo.vue'
import Settings from './Settings.vue'

const activeTab = ref('user')
</script>
<!-- UserInfo.vue -->
<template>
  <div>
    <h2>用户信息</h2>
    <p>{{ userInfo }}</p>
  </div>
</template>

<script setup>
import { ref, onActivated, onMounted } from 'vue'

const userInfo = ref('')

function fetchUserInfo() {
  console.log('请求用户数据...')
  userInfo.value = '用户:张三,年龄:28'
}

onMounted(() => {
  fetchUserInfo() // 首次加载
})
onActivated(() => {
  fetchUserInfo() // 被 keep-alive 缓存后再次激活
})
</script>
3.3 onDeactivated 简要说明(配合使用)

onDeactivated 会在组件被缓存(切换走)时触发。

onDeactivated(() => {
  console.log('组件已被缓存,不再活跃')
})

 比如:可以在 onDeactivated 中保存滚动条位置、清理定时器等。

4. keep-alive 与 onActivated 关系

特性<keep-alive>onActivated
类型内置组件生命周期钩子
功能缓存组件组件被重新激活时执行
使用方式包裹组件在组件内部调用
生命周期影响改变组件销毁行为替代 mounted 在激活时执行逻辑
配合使用YESYES

keep-alive 负责不销毁组件,onActivated 负责组件被重新激活后重新运行逻辑。二者是被动缓存(keep-alive)+ 主动唤醒(onActivated)的组合。

5. 具体实战:结合路由缓存组件页面

构建一个路由切换 + 缓存页面的案例:

路由配置

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'

const routes = [
  { path: '/', component: Home, name: 'Home' },
  { path: '/about', component: About, name: 'About' },
]

const router = createRouter({
  history: createWebHistory(),
  routes,
})

export default router

App.vue 结构

<template>
  <div>
    <router-link to="/">首页</router-link>
    <router-link to="/about">关于</router-link>

    <keep-alive>
      <router-view v-slot="{ Component }">
        <component :is="Component" />
      </router-view>
    </keep-alive>
  </div>
</template>

注意: 

如果 <router-view> 里没用 v-slot 解构 Component,那么 <keep-alive> 不会起作用。Vue Router 的 <router-view> 是动态组件,必须用 component :is="xxx" 配合。

About.vue

<template>
  <div>
    <h1>关于我们</h1>
    <p>{{ time }}</p>
  </div>
</template>

<script setup>
import { ref, onActivated, onDeactivated } from 'vue'

const time = ref('')

function updateTime() {
  time.value = new Date().toLocaleTimeString()
}

onActivated(() => {
  console.log('组件重新激活')
  updateTime()
})

onDeactivated(() => {
  console.log('组件缓存中')
})
</script>

页面不会重新加载,而是调用了 onActivated 更新时间。

6. 常见坑

1、onMounted 不会在组件被激活时再次触发

必须使用 onActivated!

// 错误:不会再触发
onMounted(() => {
  console.log('只执行一次')
})

2、组件名必须准确写入 include 才会被缓存 

确保设置了组件名:

<script>
export default {
  name: 'Home'
}
</script>

或者 defineOptions:

defineOptions({ name: 'Home' })

3、setup 代码不会重复执行

setup() 只执行一次。不要在里面写需要“每次切换回来都更新”的逻辑,要写在 onActivated 里! 

7. keep-alive 的内部工作机制

本质:基于组件的“缓存池”机制。

Vue 在创建组件时,会检查它是否被 keep-alive 包裹。若是,它就不会在卸载组件时将其销毁,而是:

  • 把组件实例保留在内存中(缓存池 cache)
  • 等再次渲染该组件时,直接复用缓存,而不是重新挂载
7.1 工作原理 & 流程图
        ↓
  <keep-alive> 包裹组件
        ↓
  检查组件的 name(或 key)
        ↓
  是否已在缓存?
        ↙         ↘
      是             否
   直接复用      正常创建组件实例
  (调用 activate)      ↓
        ↑          存入缓存 cache
        └─────<─┐
                │
        组件卸载时,拦截 unmount
                │
        不销毁,而是调用 deactivate
7.2 核心机制

keep-alive 实际上会维护一个组件缓存池:

const cache = new Map()  // 用于缓存组件 vnode

// 渲染时
if (cache.has(name)) {
  // 从缓存中取出 vnode
  vnode = cache.get(name)
  activate(vnode) // 激活缓存中的实例
} else {
  // 正常创建并挂载组件
  vnode = createComponent(...)
  cache.set(name, vnode) // 缓存起来
}

卸载时拦截(关键点):

// 正常组件卸载时会 unmount(销毁)
// 被 keep-alive 包裹的组件会触发 deactivate

与生命周期的关系

keep-alive 改变了组件的生命周期!

生命周期钩子意义何时触发
mounted挂载首次创建组件时
unmounted卸载不再显示,未被缓存
activated激活缓存中的组件重新显示
deactivated缓存离开缓存组件离开视图
7.3 性能优化建议
场景建议
大量 tab 切换页面使用 include 精准控制缓存组件
表单输入保留使用 keep-alive 避免重置数据
缓存过多设置 max 限制缓存数量
复杂嵌套组件谨慎使用嵌套 keep-alive,避免难调试
7.4 源码浅析

在 runtime-core 中可以找到 KeepAliveImpl:

const KeepAliveImpl = {
  name: `KeepAlive`,
  setup(props, { slots }) {
    const cache = new Map()
    const keys = new Set()

    const instance = getCurrentInstance()
    const sharedContext = instance.ctx

    return () => {
      const vnode = slots.default()
      const name = getComponentName(vnode.type)

      if (cache.has(name)) {
        // 命中缓存
        vnode.component = cache.get(name)
        // 激活缓存
        activate(vnode)
      } else {
        // 创建并缓存
        cache.set(name, vnode)
      }

      return vnode
    }
  }
}

最后: 

<keep-alive> 和 onActivated 是 Vue 性能优化的王牌组合。它们解决了组件频繁销毁重建的性能问题,并提供了灵活的生命周期钩子机制精准控制组件行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值