keep-alive實現原理及Vue2/Vue3對比分析

一、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異常處理的方法,通過豐富的案例和代碼,幫助新手朋…