Code前端首页关于Code前端联系我们

Vue2里的activated钩子到底怎么用?

terry 6小时前 阅读数 12 #Vue
文章标签 Vue2;activated

不少用Vue2开发项目的同学,总会在处理组件缓存、页面状态保持这些场景时碰到activated钩子,它到底是干啥的?啥时候用?和created、mounted这些又有啥不一样?今天就把activated的门道掰开揉碎讲清楚。

activated是Vue2哪个阶段的钩子?

activated是Vue2里和keep - alive强绑定的生命周期钩子,要先明白keep - alive的作用,它是Vue内置组件,能缓存组件实例,减少组件反复创建销毁带来的性能消耗。

当组件被<keep - alive>标签包裹后,其生命周期会新增“激活/缓存”逻辑:第一次渲染时,组件会执行created、mounted等常规钩子;之后只要组件未被销毁(只是被缓存到后台),再次切回显示时,activated就会触发,而created、mounted这类“初始化”钩子不会再执行(因为组件实例早已存在)。

举个例子,做手机App风格的底部Tab切换(如“首页”“我的”两个Tab),用keep - alive包裹这两个页面组件,第一次点击“首页”,首页组件会依次执行created→mounted→activated;切到“我的”,首页组件会触发deactivated(后续会讲);再切回“首页”时,首页组件不会重新执行created/mounted,而是直接触发activated——此时就能在activated里进行“切回来更新数据、重置部分状态”等操作。

activated和created、mounted有啥区别?

很多同学刚接触时,容易混淆这几个钩子的触发时机和作用,下面逐个对比:

触发时机不同

created在“组件实例刚创建完”时触发,主要用于初始化数据、绑定事件等基础操作;mounted在“模板编译成真实DOM并挂载到页面”后触发,适合进行DOM操作(如初始化echarts图表)。

只要组件被keep - alive缓存,切换时created和mounted就不再执行(因为组件实例未被销毁,只是暂时隐藏),而activated是“组件从缓存中被调出显示”时触发——不管是第一次显示(此时created、mounted、activated会依次触发),还是第N次从缓存切回显示(此时只有activated触发)。

适用场景不同

若逻辑仅需“组件第一次加载”时执行,比如初始化一个只用一次的定时器、请求永远不变的静态数据,可放在created或mounted里;

若逻辑需要“组件每次显示时”都执行(不管是第一次还是切回来),比如实时刷新的订单列表、切回来需重新请求最新库存的商品页,这时就得靠activated。

举个反例,若把“请求最新订单”的逻辑放在mounted里,第一次进入订单页确实能获取数据;但切到其他页再切回来时,mounted不会再执行,订单数据就永远是第一次的旧数据——此时把请求逻辑移到activated里,每次切回来都能重新请求,数据才会更新。

哪些场景必须用activated?

activated并非“可选”,而是“必须”的场景,往往和“组件被缓存后还要灵活更新”有关,以下是几个高频场景:

缓存组件的“状态恢复 + 局部刷新”

比如做一个带筛选条件的表格页面:用户输入搜索关键词、选择时间范围(这些状态存于组件的data中,靠keep - alive缓存不会丢失),但表格内容需每次切回来时刷新(因可能有新数据),这时在activated里编写“请求最新表格数据”的逻辑,既能保留用户的筛选状态,又能获取最新内容。

页面级定时任务的“启停控制”

假设页面有个倒计时(如秒杀活动倒计时),切走页面时倒计时要暂停(否则后台持续运行浪费性能),切回来时要继续,这时在activated里启动定时器,在deactivated(离开时的钩子)里清除定时器,二者完美配合。

依赖DOM但需“每次激活都执行”的操作

有些第三方库(如富文本编辑器、地图组件),第一次mounted时初始化没问题,但切回来后若数据变化,需重新渲染,因组件被缓存后DOM仍存在,这时在activated里调用“更新组件”的方法,就能让界面随数据变化。

再举个电商场景的例子:商品列表页和商品详情页来回切换,列表页用keep - alive缓存,从详情页切回列表页时,要刷新商品的“已售数量”(因可能刚有人下单)——此时在列表组件的activated里发请求,就能获取最新数据,提升用户体验。

用activated要避开哪些坑?

activated看似简单,实际使用时稍不注意就会出问题,以下几个“雷区”要避开:

忘记组件被keep - alive包裹

activated触发的前提是组件必须被<keep - alive>包裹,若组件未被keep - alive包裹,写了activated钩子也不会触发,且控制台无报错,排查错误时容易忽略,所以使用activated前,要先检查父组件或路由配置中是否有<keep - alive>

生命周期执行顺序搞混

当组件被keep - alive包裹时,第一次进入的生命周期顺序是:beforeCreate → created → beforeMount → mounted → activated;之后从缓存激活时,顺序是直接触发activated(离开时触发deactivated)。

若在activated里编写“依赖DOM渲染后的数据”相关逻辑,要注意第一次渲染时mounted已执行,DOM已存在,所以没问题;但若是异步获取数据后更新DOM,要确保数据请求在activated里能及时完成,避免出现“数据还未到达,DOM已渲染”的情况(可使用async/await处理异步)。

组件缓存导致的“状态污染”

keep - alive会缓存组件实例,所以组件里的data、computed等状态是复用的,比如有个“用户信息组件”,不同用户进入要显示不同信息,若不在activated里“重置数据或重新请求”,切回来时显示的还是上一个用户的信息——这时需在activated里根据路由参数,重新请求当前用户的数据,或重置data里的变量。

怎么结合keep - alive精准控制activated触发?

keep - alive本身有include(指定要缓存的组件)、exclude(指定不缓存的组件)、max(最多缓存多少个组件)等属性,合理利用这些属性能更精准地控制activated的触发:

用include/exclude指定缓存范围

比如只想让“GoodsList”组件缓存(激活时触发activated),其他组件不缓存,可这样写:

<keep - alive include="GoodsList">
  <router - view></router - view>
</keep - alive>

组件名要和组件的name选项对应(如GoodsList组件里要写name: 'GoodsList'),若为多个组件,用逗号分隔:include="GoodsList,OrderList"

动态控制缓存列表

更灵活的方式是将:include绑定为一个数组,根据路由元信息(meta)里的标记决定缓存哪些组件,比如在路由配置里给需要缓存的页面添加meta: { keepAlive: true },然后在父组件里:

<script>
export default {
  computed: {
    keepAliveComponents() {
      return this.$route.matched.filter(route => route.meta.keepAlive).map(route => route.name);
    }
  }
}
</script>
<template>
  <keep - alive :include="keepAliveComponents">
    <router - view></router - view>
  </keep - alive>
</template>

这样只有路由元信息里标记keepAlive的组件会被缓存,激活时触发activated,其他组件不缓存,activated也不触发。

用max限制缓存数量

若页面过多,缓存全部组件会占用大量内存,这时可用max限制缓存个数,如<keep - alive max="5"></keep - alive>,当缓存的组件超过5个时,最久未被使用的组件会被销毁,下次再激活该组件时,会重新执行created、mounted等初始化钩子,而非直接触发activated——这时要注意业务逻辑是否兼容这种“偶尔重新初始化”的情况。

实战:用activated做Tab切换时的状态保持与更新

光讲理论太抽象,下面以“后台管理系统的Tab切换”场景为例,看看activated如何解决“状态保持 + 数据更新”的问题。

需求场景

制作一个有“用户列表”和“订单列表”两个Tab的页面:

  1. 切换Tab时,组件要被缓存(保留用户之前的筛选条件,如搜索关键词);
  2. 每次切回Tab时,要刷新列表的最新数据(如用户在线状态、订单支付状态可能变化)。

实现步骤

父组件用keep - alive包裹Tab组件

父组件(如TabContainer.vue)里用<keep - alive>包裹动态组件,控制显示哪个Tab:

<template>
  <div class="tab - container">
    <!-- 用keep - alive缓存Tab组件 -->
    <keep - alive>
      <component :is="activeTabComponent"></component>
    </keep - alive>
    <!-- Tab切换按钮 -->
    <button @click="activeTab='UserList'">用户列表</button>
    <button @click="activeTab='OrderList'">订单列表</button>
  </div>
</template>
<script>
import UserList from './UserList.vue';
import OrderList from './OrderList.vue';
export default {
  components: { UserList, OrderList },
  data() {
    return {
      activeTab: 'UserList'
    };
  },
  computed: {
    activeTabComponent() {
      return this.activeTab;
    }
  }
}
</script>

UserList组件里写activated逻辑

在UserList.vue里,利用activated每次激活时请求最新数据,同时在mounted里也请求(保证第一次加载有数据):

<template>
  <div class="user - list">
    <input v - model="searchKey" placeholder="搜索用户">
    <ul>
      <li v - for="user in userList" :key="user.id">{{ user.name }} - {{ user.status }}</li>
    </ul>
  </div>
</template>
<script>
import axios from 'axios';
export default {
  name: 'UserList', // 要和keep - alive的include对应,所以name必须写
  data() {
    return {
      searchKey: '', // 搜索关键词,靠keep - alive缓存,切换Tab不会丢
      userList: []
    };
  },
  activated() {
    this.fetchUserList(); // 每次激活(切回来)时请求最新数据
  },
  mounted() {
    this.fetchUserList(); // 第一次加载时请求数据
  },
  methods: {
    fetchUserList() {
      axios.get('/api/users', {
        params: { keyword: this.searchKey } // 带搜索关键词请求
      }).then(res => {
        this.userList = res.data;
      });
    }
  }
}
</script>

效果验证

  • 第一次点击“用户列表”,mounted和activated都会触发,fetchUserList执行,获取初始数据;
  • 切到“订单列表”,UserList组件触发deactivated(若写了deactivated钩子,可在此处做暂停操作);
  • 再切回“用户列表”,activated触发,fetchUserList再次执行,获取最新的用户数据,而searchKey仍保留(因组件被缓存,data未被销毁)——这样就实现了“状态保持 + 数据更新”的需求。

activated和deactivated的配合使用

activated负责“组件激活时做什么”,deactivated负责“组件被缓存(离开激活状态)时做什么”,两者经常配合使用,典型场景是定时器控制

<template>
  <div class="count - down">
    <p>倒计时:{{ count }}</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 60,
      timer: null
    };
  },
  activated() {
    // 激活时启动定时器
    this.timer = setInterval(() => {
      if (this.count > 0) {
        this.count--;
      } else {
        clearInterval(this.timer);
      }
    }, 1000);
  },
  deactivated() {
    // 离开时清除定时器,避免内存泄漏
    clearInterval(this.timer);
    this.timer = null;
  }
}
</script>

这样切换Tab时,倒计时会暂停(deactivated清除定时器),切回来时又重新开始(activated启动定时器),既节省性能又保证逻辑正确。

Vue2的activated钩子是keep - alive体系里的关键角色,核心作用是让被缓存的组件在“每次激活显示”时,能执行自定义逻辑,理解它和keep - alive的绑定关系、和其他生命周期的区别,再结合实际场景(状态保持、数据刷新、资源控制)去使用,就能把组件缓存的优势发挥到极致,同时避开那些容易踩的坑,下次碰到“切页面后数据不更新”“组件缓存后状态乱掉”这类问题,不妨先想想activated能不能帮上忙~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门