Vue中的 keep-alive 实现原理

Vue中的 keep-alive 实现原理

  • keep-alive 用法
  • 实现原理
    • 源码展示
    • 源码分析

keep-alive 用法

官方文档:keep-alive 的用法

keep-alive 的作用:主要用于保留组件状态或避免重新渲染。keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

<!-- 基本用法 -->
<keep-alive><component :is="view"></component>
</keep-alive><!-- 多个条件判断的子组件使用 -->
<keep-alive><comp-a v-if="a > 1"></comp-a><comp-b v-else></comp-b>
</keep-alive>
  • Props
    • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
    • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
    • max - 数字。最多可以缓存多少组件实例。
  • 用法
    • keep-alive 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
    • 当组件在 keep-alive 标签内被切换,它的 activateddeactivated 这两个生命周期钩子函数将会被对应执行。

实现原理

keep-alivevue 源码中实现的一个组件, 我们可以从源码入手进行分析, 源码位置及链接 src/core/components/keep-alive.js

源码展示

// <keep-alive> 组件的实现也是一个对象
export default {name: 'keep-alive',// 抽象组件abstract: true,props: {// 只有名称匹配的组件才会被缓存include: patternTypes,// 任何名称匹配的组件都不会被缓存exclude: patternTypes,// 缓存组件的最大数量, 因为我们缓存的是vnode对象,它也会持有DOM,当我们缓存很多的时候,会比较占用内存,所以该配置允许我们指定缓存大小max: [String, Number]},created () {// 初始化存储缓存的cache对象和缓存 vNode 键的数组this.cache = Object.create(null)this.keys = []},//  destroyed 中销毁所有cache中的组件实例destroyed () {for (const key in this.cache) {pruneCacheEntry(this.cache, key, this.keys)}},mounted () {// 监听 include 和 exclude的变化,在变化的时候重新调整 cache的内容// 其实就是对 cache 做遍历,发现缓存的节点名称和新的规则没有匹配上的时候,就把这个缓存节点从缓存中摘除this.$watch('include', val => {pruneCache(this, name => matches(val, name))})this.$watch('exclude', val => {pruneCache(this, name => !matches(val, name))})},// 自定义render函数render () {/** 获取第一个子元素的 vnode* 由于我们也是在 <keep-alive> 标签内部写 DOM,所以可以先获取到它的默认插槽,然后再获取到它的第一个子节点。<keep-alive> 只处理第一个子元素,所以一般和它搭配使用* 的有 component 动态组件或者是 router-view,这点要牢记。*/const slot = this.$slots.defaultconst vnode: VNode = getFirstComponentChild(slot)const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {// check pattern// 判断当前组件名称和 include、exclude 的关系:const name: ?string = getComponentName(componentOptions)const { include, exclude } = this// matches就是做匹配,分别处理了数组、字符串、正则表达式的情况// 组件名如果满足了配置 include 且不匹配或者是配置了 exclude 且匹配,那么就直接返回这个组件的 vnode,否则的话走下一步缓存:if (// not included(include && (!name || !matches(include, name))) ||// excluded(exclude && name && matches(exclude, name))) {return vnode}const { cache, keys } = thisconst key: ?string = 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// 如果命中缓存,则直接从缓存中拿 vnode 的组件实例,并且重新调整了 key 的顺序放在了最后一个if (cache[key]) {vnode.componentInstance = cache[key].componentInstance// make current key freshest// 使用 LRU 缓存策略,把key移除,同时加在最后面remove(keys, key)keys.push(key)} else {// 没有命中缓存,则把 vnode设置进缓存cache[key] = vnodekeys.push(key)// prune oldest entry// 配置了max 并且缓存的长度超过了 this.max,则要从缓存中删除第一个if (this.max && keys.length > parseInt(this.max)) {// 除了从缓存中删除外,还要判断如果要删除的缓存并的组件 tag 不是当前渲染组件 tag,也执行删除缓存的组件实例的 $destroy 方法。pruneCacheEntry(cache, keys[0], keys, this._vnode)}}// keepAlive标记位vnode.data.keepAlive = true}return vnode || (slot && slot[0])}
}function pruneCacheEntry (cache: VNodeCache,key: string,keys: Array<string>,current?: VNode
) {const cached = cache[key]if (cached && (!current || cached.tag !== current.tag)) {cached.componentInstance.$destroy()}cache[key] = nullremove(keys, key)
}

源码分析

  1. keep-alive中做了以下事情:

    • 判断当前组件是否要被缓存
      获取 keep-alive 包裹的第一个子组件对象及其组件名,根据设置的 include/exclude(如果有)进行条件匹配,决定是否缓存。如果不匹配,则直接返回组件实例
    • 命中缓存则直接获取,同时更新key的位置
      根据组件idtag生成缓存 key,并在缓存对象中查找是否已缓存过该组件实例对象,如果存在,直接取出缓存值并更新该keythis.keys中的位置(更新key的位置是实现LRU置换策略的关键)
    • 不命中缓存则设置进缓存,同时检查缓存的实例数量是否超过 max
      this.cache对象中存储该组件实例并保存 key 值,之后检查缓存的实例数量是否超过 max的设置值,超过 max 的设置值,超过则根据 LRU 置换策略删除最近最久未使用的实例(即是下标为0的那个key
    • 将当前组件实例的 keepAlive 属性设置为true,这个在缓存选中过程中会用到。
  2. abstract(抽象组件)

    • 最开始设置的 abstract 属性值为 true,文档中提到过: keep-alive 是一个抽象组件,它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
    • 组件一旦被缓存,再次渲染的时候就不会执行 createdmounted 等钩子函数。
    • 有些业务场景需要在被缓存的组件重新渲染的时候需要做一些事情,vue则提供了activateddeactivated 钩子函数。
    • vue在初始化生命周期的时候,为组件实例建立父子关系时会根据 abstract 属性决定是否忽略某个组件。在keep-alive中,设置了abstract:true,那Vue就会跳过该组件实例。
  3. keep-alive 首次渲染和缓存渲染

    export function initLifecycle (vm: Component) {const options = vm.$options// locate first non-abstract parentlet parent = options.parentif (parent && !options.abstract) {while (parent.$options.abstract && parent.$parent) {parent = parent.$parent}parent.$children.push(vm)}...
    }
    
    • 首次渲染的时候,除了在 keep-alive 中建立缓存,设置vnode.data.keepAlivetrue,其他的过程和普通组件一样。
    • 缓存渲染的时候,会根据 vnode.componentInstance(首次渲染vnode.componentInstance 为 undefined) 和 vnode.data.keepAlive进行判断不会执行组件的 createdmounted 等钩子函数,而是对缓存的组件执行patch 过程,最后直接把缓存的DOM对象直接插入到目标元素中,完成了数据更新的情况下的渲染过程。
  4. LRU 缓存策略

    • LRU缓存策略:从内存中找出最久未使用的数据置换新的数据.
    • LRU(Least rencently used)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
    • 最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:
      • 新数据插入到链表头部
      • 每当缓存命中(即缓存数据被访问),则将数据移到链表头部
      • 链表满的时候,将链表尾部的数据丢弃。

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

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

相关文章

常用推理框架介绍

vLLM GitHub链接&#xff1a;https://github.com/vLLM/vllm优势&#xff1a;利用CPU的向量化指令集实现推理加速&#xff0c;适合在没有强大GPU资源的场景下使用。选择建议&#xff1a;如果你主要使用CPU进行推理&#xff0c;或者希望在不依赖GPU的情况下获得较好的性能&#…

Nacos服务注册中心

1.引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>2.application.properties中配置 # 应用名称 spring.application.namenacos-aserver…

【再探】设计模式-设计原则

设计原则是在编写程序时引导程序员遵循的一些原则和准则。这些原则旨在提高代码的可读性、可维护性、可扩展性和可重用性。 可读性&#xff1a;理解和沟通的难易程度。可维护性&#xff1a;修改和调整的难易程度。可扩展性&#xff1a;应对未来变化的能力。可重用性&#xff1…

接收区块链的CCF会议--SecureComm 2024 截止5.10 附录用率

会议名称&#xff1a;SecureComm CCF等级&#xff1a;CCF C类会议 类别&#xff1a;网络与信息安全 录用率&#xff1a;2022年录用率33%&#xff08;43/130) Topics Security and privacy in computer networks (e.g., wired, wireless, mobile, hybrid, sensor, vehicular,…

UI5:面向企业级应用的JavaScript框架

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

时间默认显示当前日期及系统时间

要将 xtdsSj 绑定到当前日期和系统时间&#xff0c;你可以在组件的 data 中初始化 xtdsSj 属性为当前日期及系统时间的字符串。然后&#xff0c;在组件创建时更新 xtdsSj&#xff0c;确保它始终显示当前日期和系统时间。 1.系统读数时间默认显示当前日期及系统时间 <templa…

斯坦福HAI年度报告增加AI4S;美阿贡国家实验室与日本最大综合研究机构建立合作;催化剂加获得深势科技未知金额投资

AI for Science 企业动态速览—— Cota Healthcare 与赛诺菲达成合作 腾讯牵头共建医疗影像国家新一代人工智能开放创新平台 催化剂加获得深势科技未知金额投资 TetraScience 与 Google Cloud 合作促进科学人工智能创新 美国阿贡国家实验室和日本理化学研究所签署谅解备忘录…

ng反向代理 conf配置

log_format szxw_timed_combined $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" $request_time $upstream_response_time;#外部转发 …

15.Nacos服务分级存储模型

服务跨集群调用问题&#xff1a; 服务调用尽可能的选择本地集群的服务&#xff0c;跨集群调用延迟较高。 本地集群不可访问的情况下&#xff0c;再去访问其他集群。 如何配置集群的实例属性&#xff1a; spring: cloud:nacos:server-addr: localhost:8848 #nacos服务端地址d…

JMeter--逻辑控制器--仅一次控制器

仅一次控制器&#xff08;Once Only Controller&#xff09; 可以让控制器内部的逻辑只执行一次&#xff1b;单次的范围是针对某一个线程&#xff0c;无论线程外面迭代多少次或者里面循环多少次&#xff0c;均只执行一次&#xff1b;单次控制器一般可用于登陆&#xff…

springCloud是什么,怎么创建

Spring Cloud是一个微服务框架&#xff0c;它为微服务架构开发提供了全套的分布式系统解决方案。它利用Spring Boot的开发便利性&#xff0c;简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等。Spring Cloud并没有…

findImg找图工具

findImg 安装 npm install findImg -g 启动 findImg run 介绍 找出当前目录下的所有图片&#xff08;包括svg的symbol格式&#xff09;在浏览器中显示出来 源码 https://github.com/HuXin957/find-img 场景 例如前端项目中的img目录&#xff0c;大家都在往里面放图片&#xff…

java接口自动化测试

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

实验3 7段数码管译码器动态显示

实验目的: 1、构建基于verilog语言的8位7段断数码管的驱动实验; 2、掌握数码管的数显原理。 3、完成如下功能:8位数码管循环显示0123456789。 实验内容及步骤: 一、实验原理 1、数码管结构 当数码管特定的段加上电压后,这些特定的段就会发亮,以形成我们眼睛看到的…

那些早期的iax和SIP软电话软件界面,看看你见过几个?

目录 一些iax/sip软电话UI图片SIP软电话的界面怎么设计SIP软电话的功能有哪些 早期voip发展中&#xff0c;很多公司开发了自己的SIP软电话&#xff0c;有些已经不存在了&#xff0c;有些还在使用中&#xff0c;比如X-Lite&#xff0c;Zoiper等等&#xff0c;我们一起看看这些早…

Linux文本处理三剑客:awk、grep和sed

Linux文本处理三剑客&#xff1a;awk、grep和sed的完美结合 在Linux世界里&#xff0c;文本处理是一项至关重要的任务。无论是日常的系统管理还是复杂的软件开发&#xff0c;都需要对文本数据进行提取、过滤和转换。Linux为我们提供了三款强大的文本处理工具&#xff1a;awk、…

Environment Modules工具

Environment Modules工具 简介 Module是一个环境变量管理工具&#xff0c;可以很好的实现开发环境的切换。 具体可以查看官网文档 安装 安装&#xff08;安装完成之后需要exit重新登录一下才会生效&#xff09; yum install -y environment-modules命令介绍 module avai…

lvgl图形化设计工具GUI Guider结合使用

前言 上篇博客整合了lvgl到项目中&#xff0c;采用的是自己编写源码的方式&#xff0c;实现了个简单的界面。实际过程中一般情况开发界面都借助设计工具&#xff0c;这里使用的是gui guider来进行示例记录 项目结构&#xff08;生成代码路径依然放到项目路径下&#xff09; C…

分别用高斯消元法和列主元消去法求解,(自制)表格比较两种算法的结果与精度,分析实验出现的问题,并总结解决办法。

以下是一个使用高斯消元法和列主元消去法求解线性方程组的示例&#xff1a; 假设我们要解决以下线性方程组&#xff1a; 4x 2y z 8 -2x y - 3z -11 3x - 2y 4z 10 首先&#xff0c;我们可以将该线性方程组表示为增广矩阵的形式&#xff1a; [4 2 1 | 8] [-2 1 -3 | …

实验2 组合逻辑电路与时序逻辑电路设计

实验目的: 1.构建基于verilog语言的组合逻辑电路和时序逻辑电路; 2.掌握verilog语言的电路设计技巧。 3.完成如下功能:加法器、译码器、多路选择器、计数器、移位寄存器等。 实验内容及步骤: 一、实验原理 原理图文件《数字系统设计_sch.pdf》,找到如下两个部分: 图…