深入理解VUE的keep-alive

1.keep-alive 功能介绍

<keep-alive>是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。

比如:实际页面业务中,申请页面可能需要填写多页或者多张表单,需要缓存已填写的表单信息,在页面或者组件切换中,数据不会被销毁。个时候就可以用到 <keep-alive>。或者是移动端项目,用户在进行返回操作时,希望保持之前的页面内用。

2.组件核心逻辑

基本思想

<keep-alive> 是一种抽象组件,被其包括的组件,如果组件的name(组件名称属性),满足include(包含) 和 exclude(不包含) 以及max(缓存组件的最大数量), 那么遵循LRU算法 ,将组件们状态存储,并且给每个组件添加新的生命周期activated(活跃状态),deactivated(熄火状态)

LRU算法

LRU是Least Recently Used的缩写,是一种页面置换算法。其核心思想是如果数据最近被访问过,那么将来被访问的几率也更高。

那么keep-alive 缓存组件时,1.如果判断缓存数组中没有该组件,且当前缓存长度已是最大,会把数组最后存放的组件踢出后再存放数组第一位。2.如果判断缓存数组中有该组件,由于LRU算法, 将缓存数组中的该组件剔除,再存放数组第一位。

新增的生命周期执行顺序

2.1. 使用<keep-alive>包裹的组件,在初次渲染时会调用组件的依次调用:created、beforeMount、mounted、activated

2.2 在组件被切换或者使用v-if = false隐藏时, 组件内部会调用deactivated生命周期。

3.核心源码分析

内部生命周期

typescript 复制代码
created () {
    /* 缓存对象  created钩子会创建一个cache对象,用来保存vnode节*/
    this.cache = Object.create(null)
},
/* destroyed 钩子则会调用pruneCacheEntry方法清除cache缓存中的所有组件实例 */
destroyed () {
    for (const key in this.cache) {
        pruneCacheEntry(this.cache[key])
    }
},

render函数

typescript 复制代码
render () {
    /* 得到slot插槽中的第一个组件 */
    const vnode: VNode = getFirstComponentChild(this.$slots.default)
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
        // 获取组件名称,优先获取组件的name字段,否则是组件的tag
        const name: ?string = getComponentName(componentOptions)
        // 不需要缓存,则返回 vnode
        if (name && (
        (this.include && !matches(this.include, name)) ||
        (this.exclude && matches(this.exclude, name))
        )) {
            return vnode
        }
        // 生成唯一标识
        const key: ?string = vnode.key == null
            ? componentOptions.Ctor.cid + (componentOptions.tag ?`::${componentOptions.tag}` : '')
            : vnode.key
        // LRU算法
        const { cache, keys } = this
	    const key: ?string = vnode.key == null
	        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
	        : vnode.key
      	if (cache[key]) {
      		// 如果存在,那么移除之前,再次存放到数组第一位。
        	vnode.componentInstance = cache[key].componentInstance
        	remove(keys, key)
        	keys.push(key)
      	} else {
      		// 如果是新的组件
        		cache[key] = vnode
        		keys.push(key)
        	if (this.max && keys.length > parseInt(this.max)) {
        	// 删除最后的数据并且把新放在第一位
          		pruneCacheEntry(cache, keys[0], keys, this._vnode)
        	}	
      	}
        // keepAlive标记
        vnode.data.keepAlive = true
    }
    return vnode
}
相关推荐
莪_幻尘10 分钟前
你的 AI Skill 越多越蠢?Token 上下文爆炸的求生指南
前端·ai编程
lichenyang45336 分钟前
从 has.echo 到异步 API 注册表:一次 ASCF API 回调不触发的排查复盘
前端
林瞅瞅43 分钟前
Nuxt3 项目部署 Nginx 防盗链后特定 JS 文件 403 问题修复方案
前端
kyriewen1 小时前
别再每次都 Google 了:我整理了前端日常最常踩的 10 个 Git 坑,附速查表
前端·javascript·git
一颗奇趣蛋1 小时前
Web 视频开发完全指南:从入门到精通
前端
非洲农业不发达2 小时前
windows终端体验大升级,让你拥有macos级别的美化
前端·后端
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十七):登录接口完善,登录页接口整合,解决跨域
前端·后端·ai编程
唐诗2 小时前
改 3 行配置,我的 Tauri dev 冷启动从 100 秒干到 4 秒
前端·客户端
SmartBoyW2 小时前
深入ECMAScript规范:彻底搞懂JS隐式类型转换与底层ToPrimitive机制
前端·javascript
牧艺2 小时前
Cursor Rules / Skills 分层设计:让 Agent 像「团队新同事」
前端·人工智能·cursor