当前位置: 首页 > news >正文

Vue2 - keep-alive 作用和原理

目录

  • 1,介绍和作用
  • 2,原理
  • 3,使用场景
    • 3.1,效果展示
    • 3.2,实现思路

1,介绍和作用

<!-- 非活跃的组件将会被缓存! -->
<keep-alive><component :is="activeComponent" />
</keep-alive>

1,是一个内部组件,用于缓存组件实例。在组件切换时,不用重新创建而是使用缓存中的组件实例。

  • 可以避免创建组件的开销。
  • 可以保留组件状态

2,有3个属性:

  • includeexclude 属性,可以控制哪些组件进入缓存。
  • max 属性可以设置最大缓存数,当缓存的实例超过该数时,vue会移除最久没有使用的组件缓存。
<!-- 以英文逗号分隔的字符串 -->
<keep-alive include="Comp1,Comp2"><component :is="view" />
</keep-alive>
<!-- 数组 -->
<keep-alive :include="['Comp1', 'Comp2']"><component :is="view" />
</keep-alive>

3,受 keep-alive 影响,内部所有嵌套的组件都有2个生命周期函数。activateddeactivated。第1次 activated 是在 mounted 之后。

4,当嵌套组件是 <router-view> 时,缓存的是路由对应的组件。

因为 <router-view> 是函数式组件,只有一个目的,生成虚拟DOM。它没有状态,不生成实例。有穿透的效果。

<keep-alive><router-view></router-view>
</keep-alive>

2,原理

源码

keep-alive 在内部维护了一个缓存对象和 keys 数组:

// keep-alive 组件的生命周期函数
created () {this.cache = Object.create(null)this.keys = []
}
  • keys 数组记录当前缓存组件的 key,如果组件没有指定,则会自动为组件生成唯一的 key

  • cache 对象以 key 为键,vnode 为值,来缓存组件对应的虚拟 DOM。

keep-alive 组件的 render 函数中,大致逻辑:

判断当前渲染的 vnode 是否有对应的缓存,有则从缓存中读取对应组件实例,没有则将其缓存。当缓存数量 > max 时,会移除掉 keys 数组的第1个元素。

// 更新逻辑其实在 update 函数中,这里结合在一起容易理解。
render(){const slot = this.$slots.default; // 获取默认插槽const vnode = getFirstComponentChild(slot); // 得到插槽中的第一个组件的 vnodeconst name = getComponentName(vnode.componentOptions); // 获取组件名字const { cache, keys } = this; // 获取当前的缓存对象和key数组const key = ...; // 获取组件的key值,若没有,会按照规则自动生成if (cache[key]) {// 有缓存// 关键:重用组件实例vnode.componentInstance = cache[key].componentInstanceremove(keys, key); // 删除key// 将 key加入到数组末尾,这样是为了保证最近使用的组件在数组中靠后,反之靠前keys.push(key); } else {// 无缓存,则进行缓存cache[key] = vnodekeys.push(key)if (this.max && keys.length > parseInt(this.max)) {// 超过最大缓存数量,移除第一个key对应的缓存pruneCacheEntry(cache, keys[0], keys, this._vnode)}}return vnode || (slot && slot[0])
}

注意到,render() 返回的就是插槽中的第一个组件的 vnode。所以页面上显示的也就是这个子组件。

3,使用场景

在后台管理系统中比较常见,比如有2种情况就会用到:

  • 侧边栏的菜单,一般对应的是路由。切换页面(路由)时想保留之前的页面信息。
  • 列表页进详情页时,想保留列表页信息,因为列表可能是通过输入框等多个过滤条件得到的,如果从详情页返回列表页再次操作会比较繁琐。

这2种情况,都可以做成 tab 选项卡。

3.1,效果展示

在这里插入图片描述

3.2,实现思路

首先维护一个【tab选项卡】的状态,比如放到 Vuex 中。不放到组件内部,是因为不好使用,因为可能不止侧边栏会添加 tab,其他地方可能也会添加页面到 tab 选项卡。

export default new Vuex.Store({modules: {tabs: {namespaced: true,state: {pageNames: [] // 选项卡的页面},mutations: {addPage(state, newPageName) {if (!state.pageNames.includes(newPageName)) {state.pageNames.push(newPageName)}},removePage(state, pageName) {const index = state.pageNames.indexOf(pageName)if (index >= 0) {state.pageNames.splice(index, 1)}}}}}
})

其他的看代码就一目了然了:(省略了CSS)

<template><div><!-- 固定在页面左侧 --><div><h1>侧边栏</h1><ul><router-linkv-for="item in $router.options.routes":key="item.path"tag="li":to="{ name: item.name }"active-class="blue"><span>{{ item.name }}</span><button @click="handleAddPage(item.name)">+</button></router-link></ul></div><!-- 页面内容区域 --><div><!-- 固定在页面顶部,左侧和侧边栏对齐 --><div v-if="$store.state.tabs.pageNames.length"><span>tab选项卡:</span><ul><router-linkv-for="pageName in $store.state.tabs.pageNames":key="pageName"tag="li"active-class="green":to="{ name: pageName }"><span>{{ pageName }}</span><button @click="handleRemovePage(pageName)">x</button></router-link></ul></div><!-- 页面内容展示区域 --><keep-alive :include="$store.state.tabs.pageNames"><router-view></router-view></keep-alive></div></div>
</template><script>
export default {methods: {handleAddPage(pageName) {this.$store.commit('tabs/addPage', pageName)},handleRemovePage(pageName) {this.$store.commit('tabs/removePage', pageName)}}
}
</script>

以上。

相关文章:

  • Sql server强制走索引
  • 【工具变量】中国各省市级是否属于“知识产权示范区”匹配数据(2010-2024年)
  • 强化学习 - Trust Region Policy Optimization (TRPO)
  • 2、互信息(Mutual Information)
  • CSS探索浏览器兼容性
  • 【C++干货铺】C++中的IO流和文件操作
  • Java基础知识-异常
  • Delphi 7 IdHTTP POST 中文乱码得解决
  • k8s实例
  • 【Linux 基础】常用基础指令(上)
  • Impala依赖组件的客户端源码下载
  • 3d gaussian splatting笔记(paper部分翻译)
  • qt的main函数(程序启动入口)
  • 大模型+自动驾驶
  • 两个近期的计算机领域国际学术会议(软件工程、计算机安全):欢迎投稿
  • 【EOS】Cleos基础
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • C++入门教程(10):for 语句
  • css选择器
  • java2019面试题北京
  • nfs客户端进程变D,延伸linux的lock
  • pdf文件如何在线转换为jpg图片
  • PHP 的 SAPI 是个什么东西
  • python3 使用 asyncio 代替线程
  • spark本地环境的搭建到运行第一个spark程序
  • SpiderData 2019年2月23日 DApp数据排行榜
  • Spring Boot MyBatis配置多种数据库
  • 当SetTimeout遇到了字符串
  • 关于springcloud Gateway中的限流
  • 开源SQL-on-Hadoop系统一览
  • 两列自适应布局方案整理
  • 免费小说阅读小程序
  • 普通函数和构造函数的区别
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 一个项目push到多个远程Git仓库
  • - 转 Ext2.0 form使用实例
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​人工智能书单(数学基础篇)
  • (1) caustics\
  • (39)STM32——FLASH闪存
  • (C语言)逆序输出字符串
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (待修改)PyG安装步骤
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转) 深度模型优化性能 调参
  • (转)德国人的记事本
  • ***详解账号泄露:全球约1亿用户已泄露
  • . Flume面试题
  • .java 指数平滑_转载:二次指数平滑法求预测值的Java代码
  • .Net 8.0 新的变化
  • .NET 表达式计算:Expression Evaluator
  • .NET简谈设计模式之(单件模式)