首页IT科技parsepath vue 源码(【手撕源码】vue2.x中keep-alive源码解析)

parsepath vue 源码(【手撕源码】vue2.x中keep-alive源码解析)

时间2025-05-24 15:24:18分类IT科技浏览3654
导读:🐱 个人主页:不叫猫先生...

🐱 个人主页:不叫猫先生

🙋‍♂️ 作者简介:前端领域新星创作者          、阿里云专家博主          ,专注于前端各领域技术                  ,共同学习共同进步     ,一起加油呀!

💫系列专栏:vue3从入门到精通                  、TypeScript从入门到实践

📢 资料领取:前端进阶资料以及文中源码可以找我免费领取

🔥 前端学习交流:博主建立了一个前端交流群       ,汇集了各路大神                  ,一起交流学习        ,期待你的加入!(文末有我wx或者私信)

一     、前世尘缘

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 } = this if (vnodeToCache) { const { tag, componentInstance, componentOptions } = vnodeToCache cache[keyToCache] = { name: _getComponentName(componentOptions), tag, componentInstance } keys.push(keyToCache) // prune oldest entry if (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.js 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() // 执行组件的destory钩子函数 } cache[key] = null // cache中对象的key设为null remove(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 组件子节点中的第一个组件 vnode const slot = this.$slots.default const vnode = getFirstComponentChild(slot) // 获取组件的配置选项对象 const componentOptions = vnode && vnode.componentOptions if (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 缓存用的 key const 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.componentInstance vnode.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 = vnode this.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算法              ,具体是什么策略呢?当数据超过了限定空间的时候对数据清理,清理的原则是对很久没有使用到过的数据进行清除          。

声明:本站所有文章             ,如无特殊说明或标注                 ,均为本站原创发布                  。任何个人或组织  ,在未征得本站同意时          ,禁止复制              、盗用、采集             、发布本站内容到任何网站                 、书籍等各类媒体平台     。如若本站内容侵犯了原著者的合法权益                  ,可联系我们进行处理       。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
python计算阶乘之和:1!+2!+3!......+n!(Python之PyArmadillo计算库的产生)