Skip to content

Vue keep-alive 原理与 include/exclude 属性详解

概述

在Vue面试中,keep-alive是一个非常常见的考点,它考察的是你对Vue性能优化和组件生命周期管理的理解。本文将深入解析keep-alive的核心原理以及includeexclude属性的使用方法。

keep-alive 是什么?

keep-alive是Vue提供的一个内置抽象组件。它本身不会渲染成一个DOM元素,也不会出现在父组件链中。它的核心作用是缓存不活动的组件实例,而不是销毁它们,从而在组件切换时保留其状态,避免重复渲染,以达到优化性能的目的。

keep-alive 的核心原理

1. 组件实例缓存

  • keep-alivecreated生命周期钩子中,它会创建一个cache对象,这个对象就像一个"仓库",用来存储需要被缓存的组件实例
  • key通常是组件的cid(组件唯一标识)或者name
  • 当一个被包裹的组件首次被创建时,keep-alive会在它的render函数中,获取到这个组件的VNode(虚拟节点),并将其缓存到cache对象中

2. DOM元素的"移动"而非"销毁"

  • 当一个被缓存的组件失活(比如,用户切换到另一个组件)时,keep-alive并不会像常规组件那样执行销毁(unmount)流程
  • 取而代之的是,组件实例和它对应的真实DOM节点会被保留在内存中
  • 当这个组件需要再次被激活时,keep-alive会直接从cache中找到对应的VNode和组件实例
  • 它不会走一遍完整的创建流程(new Vue()render等),而是直接将之前缓存的真实DOM元素从一个隐藏的父容器中"搬运"回原来的位置,并使其可见
  • 这个过程比重新创建和挂载要快得多

3. 独特的生命周期钩子

为了让开发者能够感知到组件的"激活"和"失活"状态,keep-alive为被缓存的组件提供了两个特殊的生命周期钩子:

  • activated: 在组件被激活时调用。例如,从缓存中取出并重新插入到DOM时
  • deactivated: 在组件失活时调用。例如,被从DOM中移除并放入缓存时

注意:因为组件实例没有被销毁,所以常规的createdmountedunmounted等钩子在组件切换时不会被重复触发。如果你需要在每次组件可见时执行某些逻辑(比如重新请求数据),应该放在activated钩子中。

include 和 exclude 属性详解

includeexclude属性为我们提供了更精细化的缓存控制能力,让我们可以选择性地缓存组件,而不是一股脑地全部缓存。

include 属性(包含)

作用

只有name属性匹配include值的组件才会被缓存。

值类型

可以是字符串、正则表达式,或者是一个包含这两者的数组。

匹配规则

Vue会检查组件自身的name选项。如果一个组件没有name选项,或者name不在include列表中,那么它将不会被缓存,其行为和没有使用keep-alive一样(即每次切换都会销毁和重建)。

使用场景

当你只想缓存几个特定的、需要频繁切换且状态需要保留的组件时,使用include非常合适。

html
<!-- 缓存指定名称的组件 -->
<keep-alive :include="['ComponentA', 'ComponentB']">
  <component :is="currentComponent"></component>
</keep-alive>

<!-- 使用正则表达式匹配组件名 -->
<keep-alive :include="/^Page/">
  <component :is="currentComponent"></component>
</keep-alive>

exclude 属性(排除)

作用

name属性匹配exclude值的组件将不会被缓存。

值类型

include相同,可以是字符串、正则表达式或数组。

使用场景

当你需要缓存大部分组件,只有一两个组件因为某些原因(比如每次进入都需要最新的状态)不需要缓存时,使用exclude会更方便。

html
<!-- 排除特定组件不缓存 -->
<keep-alive :exclude="['ComponentC']">
  <component :is="currentComponent"></component>
</keep-alive>

重要注意事项

  1. 优先级:如果同时提供了includeexclude,那么exclude的优先级更高
  2. 匹配目标:这两个属性都是匹配组件的name选项。所以,给需要被缓存控制的组件起一个明确的name是一个非常好的开发习惯
  3. 动态性includeexclude的值可以是动态绑定的,这意味着你可以根据应用的逻辑(比如路由信息、用户权限等)动态地改变缓存策略

实际应用示例

路由级别的缓存

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

<script>
export default {
  data() {
    return {
      cachedComponents: ['UserProfile', 'ProductList']
    }
  }
}
</script>

在缓存组件中处理数据更新

vue
<template>
  <div>
    <h2>用户列表</h2>
    <ul>
      <li v-for="user in users" :key="user.id">
        {{ user.name }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'UserList',
  data() {
    return {
      users: []
    }
  },
  created() {
    // 只在组件首次创建时执行
    console.log('UserList created')
  },
  activated() {
    // 每次组件被激活时执行
    console.log('UserList activated')
    this.fetchUsers()
  },
  deactivated() {
    // 组件失活时执行清理工作
    console.log('UserList deactivated')
  },
  methods: {
    async fetchUsers() {
      // 获取最新用户数据
      this.users = await api.getUsers()
    }
  }
}
</script>

性能优化最佳实践

  1. 合理设置缓存策略:不是所有组件都需要缓存,只缓存那些创建成本高、状态需要保留的组件
  2. 动态控制缓存:根据业务逻辑动态调整includeexclude
  3. 内存管理:适当清理不再需要的缓存,避免内存泄漏
  4. 正确使用生命周期:在activated中处理需要每次激活时执行的逻辑

总结

keep-alive通过在内存中缓存组件实例及其DOM,避免了组件的重复销毁和创建,从而提升了应用的性能和用户体验。而includeexclude属性则提供了强大的条件缓存能力,让我们能够根据实际需求,精准地控制哪些组件应该被缓存,哪些不应该,使得性能优化更加灵活和高效。

理解keep-alive的工作原理不仅能帮助你在面试中给出专业的回答,更重要的是能指导你在实际开发中做出正确的性能优化决策。

基于 VitePress 构建