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

【手撕源码】vue2.x中keep-alive源码解析

🐱 个人主页:不叫猫先生
🙋‍♂️ 作者简介:前端领域新星创作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀!
💫系列专栏:vue3从入门到精通、TypeScript从入门到实践
📢 资料领取:前端进阶资料以及文中源码可以找我免费领取
🔥 前端学习交流:博主建立了一个前端交流群,汇集了各路大神,一起交流学习,期待你的加入!(文末有我wx或者私信)

目录

  • 一、前世尘缘
  • 二、keep-alive内置组件
    • 1.缓存动态组件
    • 2.缓存路由组件
    • 3.原理解析
      • (1)keep-alive 在生命周期中做了什么?
      • (2)源码
      • (3)abstract:true
      • (4)pruneCacheEntry函数
      • (5)render
  • 三、LRU算法

一、前世尘缘

vue中内置组件keep-alive的设计思想源于HTTP中的Keep-Alive模式,Keep-Alive模式避免频繁创建、销毁链接,允许多个请求和响应使用同一个HTTP链接。
HTTP 1.0 中keep-alive默认是关闭的,需要在HTTP头加入"Connection: Keep-Alive",才能启用Keep-Alive;HTTP 1.1中默认启用Keep-Alive,如果加入"Connection: close ",才关闭。目前大部分浏览器都是用HTTP 1.1协议。

二、keep-alive内置组件

作用:动态切换组件时缓存组件实例,避免dom重新渲染。

1.缓存动态组件

当组件为componentOne时缓存该组件实例

<keep-alive :include="componentOne`" :exclude="componentTwo" :max="num"> <component :is="currentComponent"></component> 
</keep-alive>

2.缓存路由组件

注意缓存路由组件vue2.x与vue3.x有区别,vue2.x用法如下:

<keep-alive :include="componentOne`" :exclude="componentTwo" :max="num"> <router-view :is="currentComponent"></router-view> 
</keep-alive>

vue3.x用法如下:

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

3.原理解析

缓存的组件以 [key,vnode] 的形式记录,keys记录缓存的组件key,依据inclued、exclude的值,并且当超过设置的max根据LUR算法进行清除。vue2.x和vue3.x相差不大。

(1)keep-alive 在生命周期中做了什么?

  • created:初始化catch,keys。catch是一个缓存组件虚拟dom的数组,其中数组中对象的key是组件的key,value是组件的虚拟dom;keys是一个用来缓存组件的key的数组。
  • mounted:实时监听include、exclude属性的变化,并执行相应操作。
  • destroyed:删除掉所有缓存相关的数据。

(2)源码

地址:源码地址

// 源码位置:src/core/components/keep-alive.js
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) {pruneCacheEntry(this.cache, key, this.keys)}},mounted () {//查看是否有缓存没有缓存的话直接走缓存this.cacheVNode()// 这里借助 watch 监控 include  和 exclude // 如果有变化的话,则按照最新的 include 和 exclude 更新 this.cache// 将不满足 include、exclude 限制的 缓存vnode 从 this.cache 中移除  this.$watch('include', val => {pruneCache(this, name => matches(val, name))})this.$watch('exclude', val => {pruneCache(this, name => !matches(val, name))})},updated() {this.cacheVNode()},methods:{cacheVNode() {const { cache, keys, vnodeToCache, keyToCache } = thisif (vnodeToCache) {const { tag, componentInstance, componentOptions } = vnodeToCachecache[keyToCache] = {name: _getComponentName(componentOptions),tag,componentInstance}keys.push(keyToCache)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}this.vnodeToCache = null}}},render(){//下面详细介绍}
}

(3)abstract:true

设置为true时,表面该组件为抽象组件,抽象组件不会和子组件建立父子关系,组件实例会根据这个属性决定是否忽略该组件,所以并不会有节点渲染在页面中。

(4)pruneCacheEntry函数

destoryed周期中循环了所有缓存的组件,并用 pruneCacheEntry进行处理,pruneCacheEntry做了什么事?

// src/core/components/keep-alive.jsfunction pruneCacheEntry (cache: VNodeCache,key: string,keys: Array<string>,current?: VNode
) {const cached = cache[key]if (cached && (!current || cached.tag !== current.tag)) {cached.componentInstance.$destroy() // 执行组件的destory钩子函数}cache[key] = null  // cache中对象的key设为nullremove(keys, key) // 删除keys对应的元素
}

destoryed周期中,删除缓存组件的所有数组,pruneCacheEntry主要做了这几件事:

  • 遍历缓存组件集合(cach),对所有缓存的组件执行$destroy方法
  • 清除cache中key的值
  • 清除keys中的key

(5)render

render中主要做了什么?

  • 获取keep-alive组件子节点中第一个组件的vnode、componentOptions、name
  • 如果name存在且不在include中或者存在在exclude中,则返回虚拟dom。此时该组件并没有使用缓存。
  • 接下来就是上面的else情况:使用keep-alive进行组件缓存,根据组件id,tag生成组件的key,如果cache集合中存在以key为属性名的vdom,,说明组件已经缓存过,则将缓存的 Vue 实例赋值给 vnode.componentInstance,从keys中删除key,再把key push导keys中,保证当前key在keys的最后面(这是LRU算法的关键)。如果不存在则继续走下面
  • 如果cach[key]不存在则为第一次加载组件,则把vdom赋值给cach[key],key push到key
  • 如果keys的长度大于max,则进行组件缓存清理,则把不经常使用的被缓存下来的在keys中排第一位的组件清除掉,清除也是调用的pruneCacheEntry方法
render () {// 获取 keep-alive 组件子节点中的第一个组件 vnodeconst slot = this.$slots.defaultconst vnode = getFirstComponentChild(slot)// 获取组件的配置选项对象const componentOptions = vnode && vnode.componentOptionsif (componentOptions) {// 获取组件的名称const name = _getComponentName(componentOptions)const { include, exclude } = this// 如果当前的组件 name 不在 include 中或者组件的 name 在 exclude 中// 说明当前的组件是不被 keep-alive 所缓存的,此时直接 return vnode 即可if (// not included(include && (!name || !matches(include, name))) ||// excluded(exclude && name && matches(exclude, name))) {return vnode}// 代码执行到这里,说明当前的组件受 keep-alive 组件的缓存const { cache, keys } = this// 定义 vnode 缓存用的 keyconst key =vnode.key == null? // same constructor may get registered as different local components// so cid alone is not enough (#3269)componentOptions.Ctor.cid +(componentOptions.tag ? `::${componentOptions.tag}` : ''): vnode.key// 如果 cache[key] 已经存在的话,则说明当前的组件 vnode 已经被缓存过了,此时需要将其恢复还原出来if (cache[key]) {// 将缓存的 Vue 实例赋值给 vnode.componentInstancevnode.componentInstance = cache[key].componentInstance// make current key freshest// 先从 keys 中移除 key,然后再 push key,这可以保证当前的 key 在 keys 数组中的最后面remove(keys, key)keys.push(key)} else {// delay setting the cache until update// 如果 cache[key] 不存在的话,说明当前的子组件是第一次出现,此时需要将 vnode 缓存到 cache 中,将 key 存储到 keys 字符串数组中。这里是用一个中间变量接收,当数据变化时触发updated去调用cacheVNode方法。this.vnodeToCache = vnodethis.keyToCache = key}// @ts-expect-error can vnode.data can be undefined// 将 vnode.data.keepAlive 属性设置为 true,这对 vnode 有一个标识的作用,标识这个// vnode 是 keep-alive 组件的 render 函数 return 出去的,这个标识在下面的运行代码中有用vnode.data.keepAlive = true}return vnode || (slot && slot[0])}

三、LRU算法

缓存的组件在进行清除的时候使用了LRU算法,具体是什么策略呢?当数据超过了限定空间的时候对数据清理,清理的原则是对很久没有使用到过的数据进行清除

相关文章:

【手撕源码】vue2.x中keep-alive源码解析

&#x1f431; 个人主页&#xff1a;不叫猫先生 &#x1f64b;‍♂️ 作者简介&#xff1a;前端领域新星创作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab;系列专栏&#xff1a;vue3从入门…...

ROS2机器人编程简述humble-第四章-BASIC DETECTOR .3

书中程序适用于turtlebot、husky等多种机器人&#xff0c;配置相似都可以用的。支持ROS2版本foxy、humble。基础检测效果如下&#xff1a;由于缺&#xffe5;&#xff0c;所有设备都非常老旧&#xff0c;都是其他实验室淘汰或者拼凑出来的设备。机器人控制笔记本是2010年版本。…...

【图像分类】基于PyTorch搭建LSTM实现MNIST手写数字体识别(双向LSTM,附完整代码和数据集)

写在前面&#xff1a; 首先感谢兄弟们的关注和订阅&#xff0c;让我有创作的动力&#xff0c;在创作过程我会尽最大能力&#xff0c;保证作品的质量&#xff0c;如果有问题&#xff0c;可以私信我&#xff0c;让我们携手共进&#xff0c;共创辉煌。 在https://blog.csdn.net/A…...

【Linux】多线程编程 - 同步/条件变量/信号量

目录 一.线程同步 1.什么是线程同步 2.为什么需要线程同步 3.如何实现线程同步 二.条件变量 1.常见接口以及使用 2.wiat/signal中的第二个参数mutex的意义 3.代码验证 三.POSIX信号量 1.概念 2.常见接口以及使用 四.条件变量vsPOSIX信号量 一.线程同步 1.什么是线…...

ES优化方案

ES优化&联合HBASE&#xff1a; 【Elasticsearch】优秀实践-ESHbase的实现_少加点香菜的博客-CSDN博客_sceshbase ES写入性能优化方案 ElasticSearch 调优笔记_index.refresh_interval_六月飞雪的博客-CSDN博客 es如何提升写入性能_婲落ヽ紅顏誶的博客-CSDN博客_es写入性…...

从数据备份保护到完整生命周期管理平台,爱数全新发布 AnyBackup Family 8

编辑 | 宋慧 出品 | CSDN 云计算 从2003年创业&#xff0c;开始做数据备份技术&#xff0c;爱数已经走过了近20年的时间。现在&#xff0c;数据的价值被越来越多的业界与用户看到&#xff0c;数据分析应用赛道近年一直持续火热。而现在的爱数在做的&#xff0c;已经从数据的备…...

Go 微服务开发框架 DMicro 的设计思路

Go 微服务开发框架 DMicro 的设计思路 DMicro 源码地址: Gitee:dmicro: dmicro是一个高效、可扩展且简单易用的微服务框架。包含drpc,dserver等 背景 DMicro 诞生的背景&#xff0c;是因为我写了 10 来年的 PHP&#xff0c;想在公司内部推广 Go, 公司内部的组件及 rpc 协议都…...

浅谈功能测试

1.功能测试流程 1.1 功能测试流程 # 功能测试大致按照以下流程进行: (1).需求分析与评审(2).测试计划与测试方案(3).测试用例设计(4).测试用例评审(5).执行用例(6).缺陷跟踪及报告产出 1.2 功能测试流程详解 (1).需求分析与评审 功能测试应从需求出发, 功能测试就是尽量覆…...

UDP的详细解析

UDP的详细解析 文章目录UDP的详细解析UDP 概述UDP的首部格式检验和的计算抓包测试参考TCP/IP运输层的两个主要协议都是互联网的正式标准&#xff0c;即&#xff1a;用户数据报协议UDP (User Datagram Protocol)传输控制协议TCP (Transmission Control Protocol) 按照OSI的术语…...

史上最详细JUC教程之Synchronized与锁升级详解

在Java早期版本中&#xff0c;synchronized属于重量级锁&#xff0c;效率低下&#xff0c;因为监视器锁&#xff08;monitor&#xff09;是依赖于底层的操作系统的Mutex Lock来实现的&#xff0c;挂起线程和恢复线程都需要转入内核态去完成&#xff0c;阻塞或唤醒一个Java线程需…...

Vue|初识Vue

Vue是一款用于构建用户界面的JavaScript框架。它基于标准HTML、CSS和JavaScript构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助开发者高效地开发用户界面。 初识Vue1. Vue简介2. 开发准备3. 模板语法3.1 差值语法3.2 指令语法4. 数据绑定4.1 单向数据…...

在职阿里6年,一个29岁女软件测试工程师的心声

简单的先说一下&#xff0c;坐标杭州&#xff0c;14届本科毕业&#xff0c;算上年前在阿里巴巴的面试&#xff0c;一共有面试了有6家公司&#xff08;因为不想请假&#xff0c;因此只是每个晚上去其他公司面试&#xff0c;所以面试的公司比较少&#xff09;其中成功的有4家&…...

(C语言)自定义类型,枚举与联合

问&#xff1a;1. 结构体在自引用的时候不能怎么样&#xff1f;可以怎么样&#xff1f;2. Solve the problems&#xff1a;自定义一个学生结构体类型&#xff0c;要包含姓名&#xff0c;性别&#xff0c;年龄&#xff0c;六科成绩&#xff0c;家乡&#xff08;也为结构体&#…...

node.js服务端笔记文档学会写接口,学习分类:path、包、模块化、fs、express、中间件、jwt、开发模式、cors。

node.js 学习笔记 node.js服务端笔记文档学会写接口&#xff0c;path、包、模块化、fs、express、中间件、JWT、开发模式、cors。 gitee&#xff1a;代码接口笔记 1什么是node.js nodejs 是基于ChromeV8&#xff0c;引擎的一个javaScript 运行环境。node.js 无法使用DOM和BO…...

初始C++(三):引用

文章目录一.引用的概念二.引用的使用1.引用作为输出型参数2. 引用作为函数返回值3.const引用三.引用的一些小问题四.引用和指针五.引用和指针的区别一.引用的概念 引用的作用是给一个已经存在的变量取别名&#xff0c;编译器不会为引用变量开空间&#xff0c;引用变量和被他引…...

【前端】参考C站动态发红包界面,高度还原布局和交互

最近有些小伙伴咨询博主说前端布局好难&#xff0c;其实都是熟能生巧&#xff01; 模仿C站动态发红包界面&#xff0c;cssdiv实现布局&#xff0c;纯javascript实现交互效果 目录 1、界面效果 2、界面分析 2.1、整体结构 2.2、标题 2.3、表单 2.4、按钮 3、代码实现 3.…...

VR全景带你浪漫“狂飙”情人节,见证甜蜜心动

当情人节遇上VR&#xff0c;足以让情侣过一个难忘的情人节。马上情人节就要到了&#xff0c;大家是不是还在绞尽脑汁的想着&#xff0c;如何和另一半过一个浪漫的情人节呢&#xff1f;老套的剧情已经不能吸引人了&#xff0c;让我们看看VR全景给情人节带来了哪些不同的体验吧&a…...

Linux系统安全之iptables防火墙

目录 一.iptables防火墙基本介绍 二.iptables的四表五链 三.iptables的配置 1.iptables的安装 2.iptables防火墙的配置方法 四.添加、查看、删除规则 1.查看(fliter)表中的所有链 iptables -L 2.使用数字形式(fliter)表所有链 查看输出结果 iptables -nL 3.清空表中所…...

【C#基础】C# 变量与常量的使用

序号系列文章1【C#基础】C# 程序通用结构2【C#基础】C# 基础语法解析3【C#基础】C# 数据类型总结文章目录前言一. 变量&#xff08;variable&#xff09;1&#xff0c;变量定义及初始化2&#xff0c;变量的类别3&#xff0c;接收输出变量二. 常量&#xff08;constant&#xff…...

[ 常用工具篇 ] CobaltStrike(CS神器)基础(一) -- 安装及设置监听器详解

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...