一、keep-alive基本概念

keep-alive是Vue的内置组件,用于缓存组件实例,避免重复渲染。它具有以下特点:

  1. 抽象组件:自身不会渲染DOM,也不会出现在父组件链中
  2. 包裹动态组件:缓存不活动的组件实例,而不是销毁它们
  3. 生命周期:提供activated和deactivated钩子函数

二、keep-alive核心实现原理

1. 基本工作流程

  1. 判断当前组件是否需要缓存
  2. 生成组件唯一key
  3. 缓存组件实例
  4. 在被包裹组件上触发对应的生命周期钩子

2. 缓存策略

采用LRU(Least Recently Used)算法:

  • 设置最大缓存数量(max属性)
  • 优先删除最久未使用的组件
  • 新组件加入时,若达到上限则删除最旧组件

三、Vue2实现原理

// Vue2 中 keep-alive 的核心实现
export default {name: 'keep-alive',abstract: true, // 抽象组件标识props: {include: [String, RegExp, Array],exclude: [String, RegExp, Array],max: [String, Number]},created () {this.cache = Object.create(null) // 缓存对象this.keys = [] // 缓存key数组},destroyed () {// 销毁所有缓存实例for (const key in this.cache) {pruneCacheEntry(this.cache, key)}},mounted () {// 监听include和exclude的变化this.$watch('include', val => {pruneCache(this, name => matches(val, name))})this.$watch('exclude', val => {pruneCache(this, name => !matches(val, name))})},render () {const slot = this.$slots.defaultconst vnode = getFirstComponentChild(slot)const componentOptions = vnode && vnode.componentOptionsif (componentOptions) {const name = getComponentName(componentOptions)const { include, exclude } = this// 判断是否需要缓存if ((include && (!name || !matches(include, name))) ||(exclude && name && matches(exclude, name))) {return vnode}const { cache, keys } = thisconst key = vnode.key == null? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : ''): vnode.key// 命中缓存if (cache[key]) {vnode.componentInstance = cache[key].componentInstanceremove(keys, key)keys.push(key) // 调整key顺序} else {cache[key] = vnode // 缓存组件keys.push(key)// 超过max限制时清理最久未使用的组件if (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0])keys.shift()}}vnode.data.keepAlive = true}return vnode || (slot && slot[0])}
}

四、Vue3实现原理

// Vue3 中 keep-alive 的核心实现
export const KeepAliveImpl = {name: 'KeepAlive',__isKeepAlive: true,props: {include: [String, RegExp, Array],exclude: [String, RegExp, Array],max: [String, Number]},setup(props, { slots }) {const cache = new Map() // 使用Map存储缓存const keys = new Set() // 使用Set存储keysconst current = getCurrentInstance()// 缓存子树const cacheSubtree = () => {if (current.subTree) {cache.set(current.subTree.key, current.subTree)keys.add(current.subTree.key)}}// 修剪缓存const pruneCache = (filter?: (name: string) => boolean) => {cache.forEach((vnode, key) => {const name = vnode.type.nameif (name && (!filter || filter(name))) {pruneCacheEntry(key)}})}// 清理缓存条目const pruneCacheEntry = (key: CacheKey) => {const cached = cache.get(key)if (!current || !isSameVNodeType(cached, current)) {unmount(cached)}cache.delete(key)keys.delete(key)}// 监听include/exclude变化watch(() => [props.include, props.exclude],([include, exclude]) => {include && pruneCache(name => matches(include, name))exclude && pruneCache(name => !matches(exclude, name))})// 卸载时清理所有缓存onBeforeUnmount(() => {cache.forEach(cached => {unmount(cached)})})return () => {const children = slots.default?.()if (!children) return nullconst vnode = children[0]if (!vnode) return nullconst comp = vnode.typeconst name = comp.name// 检查是否应该缓存if ((props.include && (!name || !matches(props.include, name))) ||(props.exclude && name && matches(props.exclude, name))) {return vnode}const key = vnode.key == null ? comp : vnode.keyconst cached = cache.get(key)// 命中缓存if (cached) {vnode.el = cached.elvnode.component = cached.component// 标记为kept-alivevnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE} else {// 缓存新组件cache.set(key, vnode)keys.add(key)// 超过max限制时清理if (props.max && cache.size > parseInt(props.max)) {pruneCacheEntry(keys.values().next().value)}}// 标记keepAlivevnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVEreturn vnode}}
}

五、Vue2和Vue3实现差异对比

1. 数据结构

  • Vue2: 使用普通对象和数组存储缓存
    this.cache = Object.create(null)
    this.keys = []
    
  • Vue3: 使用Map和Set存储缓存
    const cache = new Map()
    const keys = new Set()
    

2. 组件实现方式

  • Vue2: 选项式API,通过created、mounted等生命周期实现
  • Vue3: 组合式API,使用setup函数实现,逻辑更集中

3. 渲染机制

  • Vue2: 在render函数中直接操作VNode
  • Vue3: 使用新的渲染器架构,更好地支持Fragment和异步组件

4. 性能优化

  • Vue3优势:
    1. 更高效的响应式系统
    2. 更智能的编译优化
    3. 更好的Tree-shaking支持
    4. 更完善的TypeScript支持

5. 生命周期钩子

  • Vue2:
    export default {activated() {},deactivated() {}
    }
    
  • Vue3:
    import { onActivated, onDeactivated } from 'vue'setup() {onActivated(() => {})onDeactivated(() => {})
    }
    

六、使用方法案例

1. Vue2中的使用方法

基础用法
<!-- App.vue -->
<template><div id="app"><keep-alive><component :is="currentComponent"></component></keep-alive></div>
</template><script>
import ComponentA from './components/ComponentA.vue'
import ComponentB from './components/ComponentB.vue'export default {name: 'App',components: {ComponentA,ComponentB},data() {return {currentComponent: 'ComponentA'}}
}
</script>
配合路由使用
// router.js
import Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [{path: '/list',component: () => import('./views/List.vue'),meta: {keepAlive: true // 需要缓存的路由}},{path: '/detail',component: () => import('./views/Detail.vue'),meta: {keepAlive: false // 不需要缓存的路由}}
]export default new VueRouter({routes
})
<!-- App.vue -->
<template><div id="app"><!-- 缓存路由组件 --><keep-alive><router-view v-if="$route.meta.keepAlive"></router-view></keep-alive><!-- 不缓存的路由组件 --><router-view v-if="!$route.meta.keepAlive"></router-view></div>
</template>
使用include和exclude
<template><div id="app"><keep-alive :include="['ComponentA', 'ComponentB']" :exclude="['ComponentC']"><router-view></router-view></keep-alive></div>
</template><script>
export default {name: 'App'
}
</script>

2. Vue3中的使用方法

基础用法
<!-- App.vue -->
<template><div id="app"><KeepAlive><component :is="currentComponent"></component></KeepAlive></div>
</template><script setup>
import { ref } from 'vue'
import ComponentA from './components/ComponentA.vue'
import ComponentB from './components/ComponentB.vue'const currentComponent = ref('ComponentA')
</script>
配合路由使用
// router.ts
import { createRouter, createWebHistory } from 'vue-router'const routes = [{path: '/list',component: () => import('./views/List.vue'),meta: {keepAlive: true}},{path: '/detail',component: () => import('./views/Detail.vue'),meta: {keepAlive: false}}
]export default createRouter({history: createWebHistory(),routes
})
<!-- App.vue -->
<template><div id="app"><RouterView v-slot="{ Component }"><KeepAlive><component :is="Component" v-if="$route.meta.keepAlive" /></KeepAlive><component :is="Component" v-if="!$route.meta.keepAlive" /></RouterView></div>
</template><script setup>
import { RouterView } from 'vue-router'
</script>
使用include和exclude
<!-- App.vue -->
<template><div id="app"><RouterView v-slot="{ Component }"><KeepAlive :include="['ListPage']" :max="10"><component :is="Component" /></KeepAlive></RouterView></div>
</template><script setup>
import { RouterView } from 'vue-router'
</script>
在组件中使用生命周期钩子
<!-- ListPage.vue -->
<template><div class="list-page"><ul><li v-for="item in list" :key="item.id">{{ item.name }}</li></ul></div>
</template><script setup>
import { ref, onActivated, onDeactivated } from 'vue'const list = ref([])// 在组件被激活时触发
onActivated(() => {console.log('组件被激活')// 可以在这里恢复组件的状态,如滚动位置
})// 在组件被停用时触发
onDeactivated(() => {console.log('组件被停用')// 可以在这里保存组件的状态
})
</script>

七、总结

  1. 核心原理相同:

    • 都使用LRU缓存策略
    • 都支持include/exclude过滤
    • 都实现了组件缓存和重用
  2. 主要改进:

    • Vue3使用更现代的数据结构
    • 更清晰的代码组织方式
    • 更好的性能优化
    • 更强大的TypeScript支持
    • 更完善的错误处理机制

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

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

相关文章

安卓jetpack compose学习笔记-Navigation基础学习

目录 一、Navigation 二、BottomNavigation Compose是一个偏向静态刷新的UI组件&#xff0c;如果不想要自己管理页面切换的复杂状态&#xff0c;可以以使用Navigation组件。 页面间的切换可以NavHost&#xff0c;使用底部页面切换栏&#xff0c;可以使用脚手架的bottomBarNav…

基于大数据技术的在UGC数据分析与路线推荐的研究

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

flask通过表单自动产生get请求的参数、form表单实现POST请求的自动提交

通过表单自动产生get请求的参数 相关代码如下&#xff1a; import flaskapp flask.Flask(__name__)app.route(/) def login():html <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><title>flask表单实现get…

《情感反诈模拟器》2025学习版

1.2 专业内容支持 67篇情感诈骗案例研究14万字心理学分析资料783条专业配音对白 二、安装与运行 2.1 系统要求 最低配置&#xff1a; 显卡&#xff1a;GTX 1060CPU&#xff1a;i5-8400存储&#xff1a;25GB空间 2.2 运行步骤 解压游戏文件&#xff08;21.7GB&#xff09;…

预训练 vs. 微调:大模型落地的核心两步,究竟有何不同?

在人工智能领域&#xff0c;尤其是自然语言处理&#xff08;NLP&#xff09;和计算机视觉&#xff08;CV&#xff09;&#xff0c;大型模型如GPT系列、BERT、Stable Diffusion等取得了令人瞩目的成就。支撑这些模型广泛应用的关键技术流程&#xff0c;通常包含两个核心阶段&…

微信原生小程序转uniapp过程及错误总结

https://ask.dcloud.net.cn/article/35786 此文章尤为重要&#xff0c;可以使用辅助工具 1、this.setData 源代码&#xff1a; this.setData({dateTime: obj.dateTime, });需更换为 this.dateTime obj.dateTime2、cookie问题 在此文章有解释 https://blog.csdn.net/ni155…

关于Spring JBDC

一、什么是Spring JDBC&#xff1f; 什么是JDBC&#xff1f; JDBC&#xff08;Java Database Connectivity&#xff09;是 Java 语言访问数据库的标准 API&#xff0c;它定义了一组接口和类&#xff0c;允许 Java 程序与各种数据库进行交互。JDBC 提供了执行 SQL 语句、处理结果…

【SpringBoot】Spring Boot实现SSE实时推送实战

以下是一个完整的基于 Spring Boot 的 Server-Sent Events (SSE) 示例&#xff0c;包括服务端和客户端的实现。 一、服务端实现 1. 创建 Spring Boot 项目 首先&#xff0c;创建一个基本的 Spring Boot 项目&#xff0c;并添加 spring-boot-starter-web 依赖。在 pom.xml 中…

若依导出模板时设置动态excel下拉框(表连接的)

若依导出模板时设置动态excel下拉框&#xff08;表连接的&#xff09; 一、问题二、解决1、实体类2.1、临时使用2.2、统一工具类3、调用 一、问题 若依导出只能&#xff1b;使用dictType、combo、comboReadDict、readConverterExp这些来控制字典的导出下拉&#xff0c;如果不是…

Rabbitmq集成springboot 使用死信队列

一、何为死信队列 RabbitMQ的死信队列&#xff08;Dead Letter Queue&#xff0c;DLQ&#xff09;是一种特殊的队列机制&#xff0c;用于处理那些无法被正常消费的消息。这些消息可能由于各种原因无法被消费者正确处理&#xff0c;如果不加以处理&#xff0c;可能会导致队列堵塞…

Spring Boot 项目中 resources 文件读取

开发必备&#xff01;Spring Boot 项目中 resources 文件读取的 9 大方案详解 在 Spring Boot 项目中&#xff0c;resources 目录承载着大量的关键资源&#xff0c;如配置文件、模板文件、脚本资源、数据文件等。而如何以合适的方式高效、安全地读取这些资源&#xff0c;往往是…

力扣-1143.最长公共子序列

题目描述 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xf…

《算法笔记》之二(笔记)

1. vector&#xff1a; 1.定义&#xff1a;“变长数组”&#xff08;长度依据需要而自动改变&#xff0c;节省空间&#xff0c;避免普通数组超内存&#xff09; 代码定义&#xff1a;vector < typename > name; 注&#xff1a;&#xff08;注意理解&#xff09; vecto…

PROFIBUS DP 转 EtherCAT 网关:冶金自动化高效协同的基石

在冶金行业高炉、连铸、轧钢等复杂场景中&#xff0c;生产设备往往跨越不同时代。许多关键产线仍依赖西门子PLC为核心的PROFIBUS DP网络&#xff0c;而新型伺服驱动器、机器人手臂则普遍采用高性能EtherCAT接口。如何实现新旧系统的无缝集成&#xff1f;JH-PB-ECT疆鸿智能PROFI…

开发云数据库

1、云数据库概述 云数据库是一款端云协同的数据库产品&#xff0c;是AGC云开发&#xff08;AGC Serverless&#xff09;关键服务之一&#xff0c;为AGC构建了MBaas&#xff08;Mobile Backend as a Service&#xff0c;移动后端即服务&#xff09;能力。云数据库提供了端云数据…

IEEE使用遇到的问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、如何在已知期刊中查找自己方向的论文 前言 IEEE 使用相关问题记录 一、如何在已知期刊中查找自己方向的论文 比如在IEEE Transactions on Visualization …

深入解析C#数组协变与克隆机制

—— 值类型与引用类型的内存行为差异 &#x1f50d; 一、数组协变&#xff08;Array Covariance&#xff09; 核心条件&#xff1a; 仅适用于引用类型数组被赋值对象与数组基类型需存在隐式/显式转换关系 class Animal {} class Dog : Animal {}Animal[] animals new Dog…

零散问题一

1.函数重载的原理 名称修饰&#xff08;Name Mangling&#xff09; 作用&#xff1a;编译器在编译时对函数名进行编码&#xff0c;生成唯一的内部标识符&#xff0c;使得同名函数能通过参数列表的差异被区分。示例&#xff1a; void func(int a); // 修饰后可能为 _Z4funcivo…

React Native【详解】内置 API

屏幕 Dimensions 获取屏幕信息 import { Dimensions } from "react-native"; export default function demo() {const { width, height, scale, fontScale } Dimensions.get("window");console.log(width, height, scale, fontScale); }参数为 window 时…

Selenium自动化测试常见的异常处理

在软件开发和测试领域,Selenium作为一种广泛使用的自动化测试工具,扮演着至关重要的角色。随着自动化测试的不断普及,如何在测试过程中有效捕获并处理异常,成为了每个测试工程师必须掌握的技能。本文旨在深入探讨Selenium异常处理的方法,通过丰富的案例和代码,帮助新手朋…