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

news/2024/10/22 17:23:30/

🐱 个人主页:不叫猫先生
🙋‍♂️ 作者简介:前端领域新星创作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀!
💫系列专栏: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算法,具体是什么策略呢?当数据超过了限定空间的时候对数据清理,清理的原则是对很久没有使用到过的数据进行清除


http://www.ppmy.cn/news/31964.html

相关文章

Pytorch实现EdgeCNN(基于PyTorch实现)

文章目录前言一、导入相关库二、加载Cora数据集三、定义EdgeCNN网络3.1 定义EdgeConv层3.1.1 特征拼接3.1.2 max聚合3.1.3 特征映射3.1.4 EdgeConv层3.2 定义EdgeCNN网络四、定义模型五、模型训练六、模型验证七、结果完整代码前言 大家好&#xff0c;我是阿光。 本专栏整理了…

【Windows】六种正确清理C盘的方法,解决你的红色烦恼

如何正确的清理C盘前言清理方法1. 利用Windows自己附带的磁盘清理工具2. 开启自动清理3. 通过“配置存储感知或立即运行”来清理4. 管理C盘中的程序5. 系统文件夹转移6. 将C盘现有内容转移到别的盘参考链接前言 Windows操作系统一般是安装在磁盘驱动器的C盘中&#xff0c;运行…

【Linux】冯.诺依曼体系结构与操作系统

环境&#xff1a;centos7.6&#xff0c;腾讯云服务器Linux文章都放在了专栏&#xff1a;【Linux】欢迎支持订阅&#x1f339;冯.诺依曼体系结构什么是冯诺依曼体系结构&#xff1f;我们如今的计算机比如笔记本&#xff0c;或者是服务器&#xff0c;基本上都遵循冯诺依曼体系结构…

英雄算法学习路线

文章目录零、自我介绍一、关于拜师二、关于编程语言三、算法学习路线1、算法集训1&#xff09;九日集训2&#xff09;每月算法集训2、算法专栏3、算法总包四、英雄算法联盟1、英雄算法联盟是什么&#xff1f;2、如何加入英雄算法联盟&#xff1f;3、为何会有英雄算法联盟&#…

100天精通Python(可视化篇)——第77天:数据可视化入门基础大全(万字总结+含常用图表动图展示)

文章目录1. 什么是数据可视化&#xff1f;2. 为什么会用数据可视化&#xff1f;3. 数据可视化的好处&#xff1f;4. 如何使用数据可视化&#xff1f;5. Python数据可视化常用工具1&#xff09;Matplotlib绘图2&#xff09;Seaborn绘图3&#xff09;Bokeh绘图6. 常用图表介绍及其…

Hadoop集群搭建

文章目录一、运行环境配置(所有节点)1、基础配置2、配置Host二、依赖软件安装(101节点)1、安装JDK2、安装Hadoop(root)3、Hadoop目录结构三、本地运行模式&#xff08;官方WordCount&#xff09;1、简介2、本地运行模式&#xff08;官方WordCount&#xff09;四、完全分布式运行…

Spring Boot 最核心的27个注解,你了解多少?

文章目录导读Spring 相关6个注解1、Configuration2、ComponentScan3、Conditional4、Import5、ImportResource6、ComponentSpring Boot 最核心的20个注解1、SpringBootApplication2、EnableAutoConfiguration3、SpringBootConfiguration4、ConditionalOnBean5、ConditionalOnMi…

【Spring】注解实现IOC操作,你理解了吗?

作者&#xff1a;狮子也疯狂 专栏&#xff1a;《spring开发》 坚持做好每一步&#xff0c;幸运之神自然会驾凌在你的身上 专栏推荐&#xff1a;写文章刚刚起步&#xff0c;各个专栏的知识点后续会补充完善&#xff0c;不断更新好文&#xff0c;希望大 家支持一下。 专栏名字El…