vue3手動刪除keepAlive緩存的方法
keepAlive的最大緩存數(shù)是無限大
當我們未設置keepAlive的最大緩存數(shù)時,當緩存組件太多,會導致內存溢出。
keepAlive最大緩存數(shù)測試實踐
- 如下腳本,給組件設置keepAlive緩存,并將組件的key設置為
$route.path
<router-view v-slot="{ Component }"> <keep-alive > <component :is="Component" :key="$route.path"/> </keep-alive> </router-view>
- 如下腳本,設置一個動態(tài)路由,不同id共用同一個組件
{ path: '/el/cascader/:id', name: 'ElCascaderDemo2', component: () => import('../views/ElCascaderDemo.vue') },
- 如下腳本, 點擊每一行,可以不斷的新增緩存組件
<script setup lang="ts"> import { useRouter } from 'vue-router' const router = useRouter() const go = (id: number) => { router.push({ path: '/el/cascader/' + id }) } </script> <template> <main> <div v-for="i in 10000" @click="go(i)" class="item"> {{ i }} </div> </main> </template>
- 下面腳本是keepAlive組件中的源碼,在內部打斷點,查看當前cache中緩存的組件數(shù)是多少。
const cacheSubtree = () => { if (pendingCacheKey != null) { cache.set(pendingCacheKey, getInnerChild(instance.subTree)); } }; onMounted(cacheSubtree); onUpdated(cacheSubtree);
- 如下圖,當cache中緩存12個組件時,內存已經達到了2G,
如下圖,在繼續(xù)加壓后,內存達到4019MB時,頁面崩潰。
故在使用keepAlive緩存組件,一定要設置它的最大緩存數(shù)。
設置keepAlive最大緩存數(shù)
<router-view v-slot="{ Component }"> <keep-alive :max="10"> <component :is="Component" :key="$route.path"/> </keep-alive> </router-view>
設置最大緩存數(shù)等于10以后,keepAlive組件內緩存變量cache的size<=9,內存位置在1900MB。
實現(xiàn)手動刪除緩存組件
實現(xiàn)手動刪除緩存組件的方式是:動態(tài)增刪keepAlive組件的exclude屬性。exclude屬性的值可以是一個組件名稱組成的數(shù)組。在 3.2.34 或以上的版本中,使用 <script setup>
的單文件組件會自動根據(jù)文件名生成對應的 name
選項,無需再手動聲明。但目前組件的key等于route.path
,/el/cascader/1
和/el/cascader/2
會緩存兩份,手動刪除這類組件,需要給它們各自一個名稱,而不是都用它們指向的那一個組件的名稱。
第一步:封裝動態(tài)組件component的is屬性的賦值,使用route.path作為組件的名稱。
<router-view v-slot="{ Component }"> <keep-alive :max="10"> <component :is="formatComponentInstance(Component, $route?.path)" :key="$route.path"/> </keep-alive> </router-view> let wrapperMap = new Map() const formatComponentInstance = (component : Component, path:string ) => { let wrapper if (wrapperMap.has(path)) { wrapper = wrapperMap.get(path) } else { wrapper = { name: path, render(){ return h(component) // h的第一個參數(shù)可以是字符串,也可以是一個組件定義;h返回的是一個虛擬dom } } wrapperMap.set(path, wrapper) } return h(wrapper) }
第二步:實現(xiàn)一個簡單內頁簽,內頁簽關閉時清除組件緩存
<template> <el-tag v-for="item in editableTabs" :key="item.key" closable :disable-transitions="false" @click="tabChange(item.key)" @close="handleTabsEdit(item.key, 'remove', undefined)" :type="editableKey === item.key ? 'primary':'info'" > {{ item.title }} </el-tag> </template> <script lang="ts" setup> import { ref, watch } from 'vue' import type { Ref } from 'vue' import { ElTag} from 'element-plus' import { useRoute, useRouter} from 'vue-router' const route = useRoute() const router = useRouter() import { useKeepAlive } from '../stores/index' let tabIndex = 1 const editableKey = ref('') const editableTabs : Ref<Array<{[key: string]: any}>> = ref([]) const visitedRoute: String [] = [] watch(() => route.path, (val) => { if (visitedRoute.indexOf(val) === -1) { visitedRoute.push(val) handleTabsEdit(undefined, 'add', val) } else { editableKey.value = editableTabs.value.filter((tab) => tab.title === val)[0].key } }) const handleTabsEdit = ( targetKey: string | undefined, action: 'remove' | 'add', newTabTitle: string | undefined ) => { if (action === 'add') { const newTabName = `${++tabIndex}` editableTabs.value.push({ title: newTabTitle, key: newTabName, }) editableKey.value = newTabName } else if (action === 'remove') { const tabs = editableTabs.value let activeKey = editableKey.value if (activeKey === targetKey) { tabs.forEach((tab, index) => { if (tab.key === targetKey) { // 同步刪除visitedRoute數(shù)組 let includeIndex = visitedRoute.indexOf(tab.title) visitedRoute.splice(includeIndex, 1) // 重置active tab const nextTab = tabs[index + 1] || tabs[index - 1] if (nextTab) { activeKey = nextTab.name router.push({ path: nextTab.title }) } // /頁簽關閉時,重置store中的exclude數(shù)組,如['/el/cascader/2'] const keepAliveStore = useKeepAlive() let exclude: string[] = [tab.title] keepAliveStore.setExclude(exclude) } }) } editableKey.value = activeKey editableTabs.value = tabs.filter((tab) => tab.key !== targetKey) } } const tabChange = (activeKey: string) => { let path = editableTabs.value.filter((tab) => tab.key === activeKey)[0].title router.push({ path }) } </script>
const keepAliveStore = useKeepAlive() const excludes = computed(() => { return keepAliveStore.exclude }) <router-view v-slot="{ Component }"> <keep-alive :max="10" :exclude="excludes"> <component :is="formatComponentInstance(Component, $route?.path)" :key="$route.path"/> </keep-alive> </router-view>
在調試中出現(xiàn)以下報錯:
Uncaught (in promise) TypeError: parentComponent.ctx.deactivate is not a function
報錯的原因是因為我將上面的封裝簡化為以下腳本, 導致同一個path兩次產生的組件vnode的type不一樣。為什么要簡化呢, 因為考慮到wrapperMap會有一定的內存消耗。
const formatComponentInstance = (component : Component, path:string ) => { let wrapper = { name: path, render(){ return h(component) // h的第一個參數(shù)可以是字符串,也可以是一個組件定義;h返回的是一個虛擬dom } } return h(wrapper) }
在vue源碼中的isSameVNodeType
中的n1.type === n2.type
的判斷中, 組件轉化的vnode的type是一個對象,對于相同的route.path, 每次通過上面的封裝腳本產生新的組件實例,會導致每次產生的vnode的type對象不是同一個對象,導致n1.type不等于n2.type, isSameVNodeType返回false,會卸載n1,卸載時就產生了上面的報錯。
function isSameVNodeType(n1, n2) { if (n2.shapeFlag & 6 && hmrDirtyComponents.has(n2.type)) { n1.shapeFlag &= ~256; n2.shapeFlag &= ~512; return false; } return n1.type === n2.type && n1.key === n2.key; }
以上就是vue3手動刪除keepAlive緩存的方法的詳細內容,更多關于vue3刪除keepAlive緩存的資料請關注腳本之家其它相關文章!
相關文章
vue自定義指令實現(xiàn)元素滑動移動端適配及邊界處理
這篇文章主要為大家介紹了vue自定義指令實現(xiàn)元素滑動移動端適配及邊界處理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09詳解Vue基于vue-quill-editor富文本編輯器使用心得
這篇文章主要介紹了Vue基于vue-quill-editor富文本編輯器使用心得,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01