keep-alive的应用和底层实现原理的探索

一、概念

  • keep-alive 是 Vue.js 中的一个内置组件,它用于缓存组件的状态或避免对组件进行多次销毁和重建。通过使用 keep-alive 组件,可以在组件切换时将状态保留在内存中,以便在下次需要时直接复用,从而提高性能并改善用户体验。

  • 具体来说,当一个被 keep-alive 包裹的组件被切换出去时,它的状态会被保留,而不会被销毁。当再次切换回来时,该组件的状态会被恢复,避免了重新渲染和重新初始化。这对于包含大量动态数据、复杂计算或需要长时间初始化的组件来说,可以显著减少页面加载时间和提升用户体验。

  • 在 Vue.js 中使用 keep-alive 组件通常需要配合动态组件()或者路由来实现组件的切换和缓存。在实际开发中,常见的应用场景包括标签页切换、路由切换、模态框等需要保持状态的场景。

二、使用场景

<template><keep-alive :include="whiteList" :exclude="blackList" :max="count"><component :is="component"></component></keep-alive>
</template>

在路由中使用 keep-alive

<template><!-- vue2写法 --><keep-alive :include="whiteList" :exclude="blackList" :max="count"><router-view></router-view></keep-alive><!-- vue3写法 --><router-view v-slot="{ Component }"><keep-alive :include="whiteList" :exclude="blackList" :max="count"><component :is="Component" /></keep-alive></router-view>
</template>

也可以通过 meta 属性指定哪些页面需要缓存,哪些不需要

<template><!-- vue2写法 --><keep-alive><!-- 需要缓存的视图组件 --><router-view v-if="$route.meta.keepAlive"></router-view></keep-alive><!-- 不需要缓存的视图组件 --><router-view v-if="!$route.meta.keepAlive"></router-view><!-- vue3写法 --><router-view v-slot="{ Component }"><transition><keep-alive><!-- 需要缓存的视图组件 --><component :is="Component" v-if="route.meta.keepAlive" /></keep-alive><!-- 不需要缓存的视图组件 --><component :is="Component" v-if="!route.meta.keepalive" /></transition></router-view>
</template>

四、底层原理的实现

  • vue2 底层原理的实现
export default {name: 'keep-alive',abstract: true, // 防止组件实例被创建props: {include: patternTypes, // 白名单,指定哪些组件需要缓存exclude: patternTypes, // 黑名单,指定哪些组件不需要缓存max: [String, Number] // 缓存的最大个数},created () {this.cache = Object.create(null) // 缓存组件实例的对象this.keys = []  // 缓存的组件实例的键列表},destroyed () {for (const key in this.cache) { // keep-alive组件销毁时,删除所有缓存的组件实例pruneCacheEntry(this.cache, key, this.keys)}},mounted () { // 监控include和exclude属性的变化,根据变化来对缓存进行调整this.$watch('include', val => {pruneCache(this, name => matches(val, name))})this.$watch('exclude', val => {pruneCache(this, name => !matches(val, name))})},render () {const slot = this.$slots.defaultconst vnode = getFirstComponentChild(slot) // 获取slot中的第一个组件节点const componentOptions = vnode && vnode.componentOptions // 获取组件的选项if (componentOptions) {const name = getComponentName(componentOptions) // 获取组件名const { include, exclude } = thisif (// 不在白名单中(include && (!name || !matches(include, name))) ||// 在黑名单中(exclude && name && matches(exclude, name))) {return vnode // 不需要缓存的组件直接返回}const { cache, keys } = thisconst key = vnode.key == null? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') // 生成缓存的键: vnode.keyif (cache[key]) { // 如果有缓存,直接复用组件实例vnode.componentInstance = cache[key].componentInstanceremove(keys, key)keys.push(key) // LRU算法,最近使用的组件放在数组末尾} else {cache[key] = vnode // 缓存组件实例keys.push(key)if (this.max && keys.length > parseInt(this.max)) { // 如果超出最大缓存数,删除最久未使用的组件实例pruneCacheEntry(cache, keys[0], keys, this._vnode)}}vnode.data.keepAlive = true // 在组件VNode的data中增加keep-alive属性,表示该组件已被缓存}return vnode || (slot && slot[0]) // 返回处理后的VNode或原始的slot内容}
}
  • vue3 底层原理的实现
const KeepAliveImpl: ComponentOptions = {name: `KeepAlive`,__isKeepAlive: true,props: {include: [String, RegExp, Array], // 指定需要缓存的组件名称规则exclude: [String, RegExp, Array], // 指定不需要缓存的组件名称规则max: [String, Number]  // 缓存的最大组件实例数量},setup(props: KeepAliveProps, { slots }: SetupContext) {const instance = getCurrentInstance()! // 获取当前组件实例const sharedContext = instance.ctx as KeepAliveContext // 获取共享上下文// 创建缓存组件实例的 Map 和键集合const cache: Cache = new Map()const keys: Keys = new Set()let current: VNode | null = nullconst parentSuspense = instance.suspenseconst {renderer: {p: patch,m: move,um: _unmount,o: { createElement }}} = sharedContextconst storageContainer = createElement('div') // 创建一个用于存储组件对应DOM的容器// 激活组件时调用的方法sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {const instance = vnode.component!move(vnode, container, anchor, MoveType.ENTER, parentSuspense)// ... 省略部分代码}// 失活组件时调用的方法sharedContext.deactivate = (vnode: VNode) => {const instance = vnode.component!move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)// ... 省略部分代码}// 卸载组件function unmount(vnode: VNode) {// 重置 shapeFlag 以便能够正确卸载resetShapeFlag(vnode)_unmount(vnode, instance, parentSuspense, true)}// 处理缓存function pruneCache(filter?: (name: string) => boolean) {cache.forEach((vnode, key) => {const name = getComponentName(vnode.type as ConcreteComponent)if (name && (!filter || !filter(name))) {pruneCacheEntry(key)}})}// 移除头部缓存function pruneCacheEntry(key: CacheKey) {const cached = cache.get(key) as VNodeif (!current || !isSameVNodeType(cached, current)) {unmount(cached)} else if (current) {resetShapeFlag(current)}cache.delete(key)keys.delete(key)}// 监听 include 和 exclude 属性的变化,然后清理缓存watch(() => [props.include, props.exclude],([include, exclude]) => {include && pruneCache(name => matches(include, name))exclude && pruneCache(name => !matches(exclude, name))},// 在 `current` 更新后进行清理{ flush: 'post', deep: true })// 在渲染后缓存子树let pendingCacheKey: CacheKey | null = nullconst cacheSubtree = () => {if (pendingCacheKey != null) {cache.set(pendingCacheKey, getInnerChild(instance.subTree))}}onMounted(cacheSubtree)onUpdated(cacheSubtree)// ... 省略部分代码return () => {// ... 省略部分代码if (cachedVNode) {vnode.el = cachedVNode.el // 复用缓存的 DOMvnode.component = cachedVNode.componentif (vnode.transition) {// 递归更新子树上的过渡钩子setTransitionHooks(vnode, vnode.transition!)}// 避免 vnode 被作为新的组件实例被挂载vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE// LRU算法keys.delete(key)keys.add(key)} else {keys.add(key)// 移除最旧的缓存if (max && keys.size > parseInt(max as string, 10)) {pruneCacheEntry(keys.values().next().value)}}// 避免组件被卸载vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVEcurrent = vnodereturn isSuspense(rawVNode.type) ? rawVNode : vnode}}
}

小结: 核心原理就是缓存 + LRU 算法

五、keep-alive 中数据更新问题

beforeRouteEnter: 在有 vue-router 的项目,每次进入路由的时候,都会执行beforeRouteEnter

beforeRouteEnter(to, from, next){next(vm=>{vm.getData()  // 获取数据})
},

actived: 在keep-alive缓存的组件被激活的时候,都会执行actived钩子

activated(){this.getData() // 获取数据
},

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/19823.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HackTheBox-Machines--Bashed

Bashed 测试过程 1 信息收集 NMAP 80 端口 目录扫描 http://10.129.155.171/dev/phpbash.min.php http://10.129.155.171/dev/phpbash.php 半交互式 shell 转向 交互式shell python -c import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.co…

代码随想录算法训练营第38天 | 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

代码随想录算法训练营第38天 | 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯 理论基础自己看到题目的第一想法看完代码随想录之后的想法 链接: 509. 斐波那契数 链接: 70. 爬楼梯 链接: 746. 使用最小花费爬楼梯 理论基础 五部曲&#xff1a; 1.确定dp数组&#xf…

优化CPU占用率及内存占用2

在标准化无线通信板时&#xff0c;关注过程序占用ram的问题&#xff0c;当时 发现每一个线程都会分配8M栈空间&#xff0c;这次换rk3568后&#xff0c;偶尔看了下RAM占用&#xff0c;吓了一跳&#xff0c;不但每个线程有8M栈空间&#xff0c;几乎每个线程都占用了64MB的一个RAM…

AOP进阶

黑马程序员JavaWeb开发教程 文章目录 一、通知类型1.1 通知类型1.2 注意事项1.3 PointCut 二、通知顺序2.1 执行顺序 三、切入点表达式3.1 切入点表达式3.2 切入点表达式-execution3.2 切入点表达式- annotation 四、连接点4.1 连接点 一、通知类型 1.1 通知类型 Around&…

浩江星灿面试(c++)

量化工程师&#xff1a;提供实时的数据&#xff0c;为炒股提供依据&#xff1b;稳定&#xff0c;快&#xff0c;准确&#xff1b; 对于性能的要求比较高&#xff1b; 文章目录 题目一、延迟最低的IPC(Inter-Process Communication)通信方式是什么&#xff1f;题目二、找出下面…

部署专属网页版ChatGPT-Next-Web

背景 工作学习中经常使用chat-gpt, 需求是多端使用gpt问答&#xff0c;因此搭建一个网页版本方便多个平台使用。最后选择了 ChatGPT-Next-Web 部署说明 一键部署自己的web页面&#xff0c;因为是使用免费的vercel托管的&#xff0c;vercel节点在全球都有&#xff0c;理论上突…

【YOLOv8改进[Conv]】使用YOLOv9中的Adown模块改进Conv模块的实践 + 含全部代码和修改方式 + 有效涨点

本文中进行使用YOLOv9中的Adown模块改进Conv模块的实践 ,文中包含全部代码和修改方式 ,有效涨点。 目录 一 YOLOv9 1 信息丢失问题 2 PGI ① 信息瓶颈 ② 可逆函数<

oracle 12c DB卸载流程

1.运行卸载程序 [rootprimary1 ~]# su - oracle [oracleprimary1 ~]$ cd $ORACLE_HOME/deinstall [oracleprimary1 deinstall]$ ./deinstall Checking for required files and bootstrapping ... Please wait ... 这里选择3 、回车、y、y、回车、ASM 这里输入y 2.删除相关目录…

Midjourney应用:电商模特换装

今天我们应用的是Midjourney应用&#xff1a;电商模特换装 网上找到一件衣服&#xff0c;没有模特 方法一&#xff1a;两图片融合&#xff0c;BLEND命令&#xff0c;效果不是很理想失真 方法二&#xff1a;服装图片垫图说明细节缺失https://cdn.discordapp.com/attachments/1…

Iphone自动化指令每隔固定天数打开闹钟关闭闹钟(一)

注意&#xff1a;因为是第一次用iphone的快捷指令&#xff0c;不是很明白&#xff0c;所以之后多次运行发现有bug&#xff0c;所以快捷指令部分在下一章重新写&#xff0c;我用两个日期测试了&#xff0c;没问题&#xff0c;这一章可以当做熟悉快捷指令的一些操作用&#xff0c…

STM32高级控制定时器之输入捕获模式

目录 概述 1 输入捕获模式 1.1 原理介绍 1.2 实现步骤 1.3 发生输入捕获流程 2 使用STM32Cube配置工程 2.1 软件环境 2.2 配置参数 2.3 生成项目文件 3 功能实现 3.1 PWM调制占空比函数 3.2 应用函数库 4 测试 4.1 功能框图 4.2 运行结果 源代码下载地址&#xf…

【Word】调整列表符号与后续文本的间距

1. 默认的列表格式&#xff1a; 2. 修改间距&#xff1a; ************************************************** 分割线 ************************************************************ 3. 效果

推荐一款开源电子签章/电子合同系统

文章目录 前言一、项目介绍二、项目地址三、技术架构四、代码结构介绍五、功能模块六、功能界面首页面手写签名面板电子印章制作数字证书生成 总结 前言 大家好&#xff01;我是智航云科技&#xff0c;今天为大家分享一个免费开源的电子签字系统。 一、项目介绍 开放签电子签…

热门新游 2024 植物大战僵尸杂交版 Mac 版本下载安装详细教程

最近植物大战僵尸杂交版可谓是非常的火&#xff0c;好多主播都在播这款游戏&#xff0c;我一个 Mac 党也想玩&#xff0c;可奈何该游戏目前只有 PC 版本&#xff0c;经过一番折腾终于在我的 Mac 上安装上了该游戏&#xff0c;分享给大家 其实安装过程也很简单&#xff0c;只需…

AI视频下载:ChatGPT数据科学与机器学习课程

ChatGPT是一个基于OpenAI开发的GPT-3.5架构的AI对话代理。作为一种语言模型,ChatGPT能够理解并对各种主题生成类似人类的响应,使其成为聊天机器人开发、客户服务和内容创作的多用途工具。 此外,ChatGPT被设计为高度可扩展和可定制的,允许开发人员对其响应进行微调并将其集成到…

# SpringBoot 如何让指定的Bean先加载

SpringBoot 如何让指定的Bean先加载 文章目录 SpringBoot 如何让指定的Bean先加载ApplicationContextInitializer使用启动入口出注册配置文件中配置spring.factories中配置 BeanDefinitionRegistryPostProcessor使用 使用DependsOn注解实现SmartInitializingSingleton接口使用P…

数组的应用-24点游戏

目录 24点游戏 游戏规则 游戏主要分为三部分 电脑出牌 用户输入算式 电脑判断胜负 总结 24点游戏 游戏规则&#xff1a; 54张扑克抽出大小王&#xff0c;剩余52张用来用于游戏&#xff1b;每一轮从52张牌中随机抽出4张&#xff1b;玩家运用加&#xff0c;减&#xff0…

JUC常见类

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:Java锁的策略&#x1f649; &#x1f439;今日诗词:苟利国家生死以,岂因祸福避趋之&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64…

Windows系统WDS+MDT网络启动自动化安装

Windows系统WDS+MDT网络启动自动化安装 适用于在Windows系统上WDS+MDT网络启动自动化安装 1. 安装准备 1.下载windows server 2019、windows 10 pro的ISO文件,并安装好windows server 2019 2.下载windows 10 2004版ADK及镜像包 1.1 安装平台 Windows 111.2. 软件信息 软件…

模型优化——模型剪枝、模型量化、知识蒸馏

1.模型剪枝 1.1什么是模型剪枝&#xff1f; 深度学习网络模型从卷积层到全连接层存在着大量冗余的参数&#xff0c;大量神经元激活值趋近于0&#xff0c;将这些神经元去除后可以表现出同样的模型表达能力&#xff0c;这种情况被称为过参数化&#xff0c;而对应的技术则被称为模…