1、keepalive功能
keepalive是vue3中的一個全域元件
keepalive 本身不會渲染出來,也不會出現在dom節點當中,但是它會被渲染為vnode,透過vnode可以追蹤到keepalive中的cache和keys,當然也是在開發環境才可以,build打包以後沒有暴露到vnode中(這個還要再確認一下)
keepalive 最重要的功能就是快取元件
#keepalive 透過LRU快取淘汰策略來更新元件緩存,可以更有效的利用內存,防止記憶體溢出,原始碼中的最大快取數max為10,也就是10個元件之後,就開始淘汰最先被快取的元件了
2、keepalive使用場景
-
這裡先假設一個場景: A頁面是首頁=====> B頁面清單頁面(需要快取的頁面)=======> C 詳情頁由C詳情頁到到B頁面的時候,要回到B的快取頁面,包括頁面的基礎資料和清單的捲軸位置資訊如果由B頁面返回到A頁面,則需要將B的快取頁清空
上述另一個場景:進入頁面直接緩存,然後就結束了,這個比較簡單本文就不討論了
3、在專案中的使用過程
keepalive元件總共有三個參數
#include:可傳字串、正規表示式、數組,名稱匹配成功的元件會被快取
exclude:可傳字串、正規表示式、數組,名稱符合成功的元件不會被快取
max:可傳數字,限制快取元件的最大數量,預設為10
#首先在App.vue根程式碼中加入引入keepalive元件,透過這裡可以發現,我在這裡快取的相當於整個頁面,當然你也可以進行更細緻的控制頁面當中的某個區域元件
<template> <router-view v-slot="{ Component }"> <keep-alive :include="keepAliveCache"> <component :is="Component" :key="$route.name" /> </keep-alive> </router-view> </template> <script lang="ts" setup> import { computed } from "vue"; import { useKeepAliverStore } from "@/store"; const useStore = useKeepAliverStore(); const keepAliveCache = computed(() => { return useStore.caches; }); </script>
透過App.vue可以發現,透過pinia(也就是vue2中使用的vuex)來保存要快取的頁面元件, 來處理include緩存,和保存頁面元件中的滾動條資訊資料
import { defineStore } from "pinia"; export const useKeepAliverStore = defineStore("useKeepAliverStore", { state: () => ({ caches: [] as any, scrollList: new Map(), // 缓存页面组件如果又滚动条的高度 }), actions: { add(name: string) { this.caches.push(name); }, remove(name: string) { console.log(this.caches, 'this.caches') this.caches = this.caches.filter((item: any) => item !== name); console.log(this.caches, 'this.caches') }, clear() { this.caches = [] } } });
元件路由剛切換時,透過beforeRouteEnter將元件寫入include, 此時元件生命週期還沒開始。如果都已經開始執行元件生命週期了,再寫入就意義了。
所以這個鉤子函數就不能寫在setup中,要單獨提出來寫。當然你也可以換成路由的其他鉤子函數處理beforeEach,但這裡面使用的話,好像使用不了pinia,這個還需要進一步研究一下。
import { useRoute, useRouter, onBeforeRouteLeave } from "vue-router"; import { useKeepAliverStore } from "@/store"; const useStore = useKeepAliverStore() export default { name:"record-month", beforeRouteEnter(to, from, next) { next(vm => { if(from.name === 'Home' && to.name === 'record-month') { useStore.add(to.name) } }); } } </script>
元件路由離開時判斷,是否要移出緩存,這個鉤子就直接寫在setup裡就可以了。
onBeforeRouteLeave((to, from) => { console.log(to.name, "onBeforeRouteLeave"); if (to.name === "new-detection-detail") { console.log(to, from, "进入详情页面不做处理"); } else { useStore.remove(from.name) console.log(to, from, "删除组件缓存"); } });
在keepalive兩個鉤子函數中進行處理scroll位置的快取,onActivated中取得快取中的位置, onDeactivated記錄位置到快取
onActivated(() => { if(useStore.scrollList.get(routeName)) { const top = useStore.scrollList.get(routeName) refList.value.setScrollTop(Number(top)) } }); onDeactivated(() => { const top = refList.value.getScrollTop() useStore.scrollList.set(routeName, top) });
這裡定義一個方法,設定scrollTop使用了原生javascript的api
const setScrollTop = (value: any) => { const dom = document.querySelector('.van-pull-refresh') dom!.scrollTop = value }
同時高度怎麼取得要先註冊scroll事件,然後透過getScrollTop 取得目前捲軸的位置進行儲存即可
onMounted(() => { scrollDom.value = document.querySelector('.van-pull-refresh') as HTMLElement const throttledFun = useThrottleFn(() => { console.log(scrollDom.value?.scrollTop, 'addEventListener') state.scrollTop = scrollDom.value!.scrollTop }, 500) if(scrollDom.value) { scrollDom.value.addEventListener('scroll',throttledFun) } }) const getScrollTop = () => { console.log('scrollDom.vaue', scrollDom.value?.scrollTop) return state.scrollTop }
上面註冊scroll事件中使用了一個useThrottleFn ,這個類別庫是@vueuse/core中提供的,其中封裝了很多工具都非常不錯,用興趣的可以研究研究
https://vueuse.org/shared/usethrottlefn/#usethrottlefn
此時也可以查看找到實例的vnode查找到keepalive,是在keepalive緊鄰的子元件裡
const instance = getCurrentInstance() console.log(instance.vnode.parent) // 这里便是keepalive组件vnode // 如果是在开发环境中可以查看到cache对象 instance.vnode.parent.__v_cache // vue源码中,在dev环境对cache进行暴露,生产环境是看不到的 if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { ;(instance as any).__v_cache = cache }
4、vue3 keepalive原始碼偵錯
1、複製程式碼
git clone git@github.com:vuejs/core.git
2、安裝相依
pnpm i
3 、如果無法使用pnpm,可以先透過npm安裝一下
npm i pnpm -g
4、安裝完成以後,找到根目錄package.json檔案中的scripts
// 在dev命令后添加 --source-map是从已转换的代码,映射到原始的源文件 "dev": "node scripts/dev.js --sourcemap"
參考 https://www.yisu .com/article/154583.htm
5、執行pnpm run dev則會build vue原始碼
pnpm run dev //则会出现以下,代表成功了(2022年5月27日),后期vue源代码作者可能会更新,相应的提示可能发生变更,请注意一下 > @3.2.36 dev H:\github\sourceCode\core > node scripts/dev.js --sourcemap watching: packages\vue\dist\vue.global.js //到..\..\core\packages\vue\dist便可以看到编译成功,以及可以查看到examples样例demo页面
6、然後在....\core\packages\vue\examples\composition中加入一個aehyok.html文件,將如下程式碼進行拷貝,然後透過chrome瀏覽器打開,F12,找到原始碼的Tab頁面,透過快捷鍵Ctrl P 輸入KeepAlive便可以找到這個元件,然後透過左側行標右鍵就可以加入斷點,進行調試,也可以透過右側的【呼叫堆疊】進行快速跳轉程式碼進行調試。
<script src="../../dist/vue.global.js"></script> <script type="text/x-template" id="template-1"> <div>template-1</div> <div>template-1</div> </script> <script type="text/x-template" id="template-2"> <div>template-2</div> <div>template-2</div> </script> <script> const { reactive, computed } = Vue const Demo1 = { name: 'Demo1', template: '#template-1', setup(props) { } } const Demo2 = { name: 'Demo2', template: '#template-2', setup(props) { } } </script> <!-- App template (in DOM) --> <div id="demo"> <div>Hello World</div> <div>Hello World</div> <div>Hello World</div> <button @click="changeClick(1)">组件一</button> <button @click="changeClick(2)">组件二</button> <keep-alive :include="includeCache"> <component :is="componentCache" :key="componentName" v-if="componentName" /> </keep-alive> </div> <!-- App script --> <script> Vue.createApp({ components: { Demo1, Demo2 }, data: () => ({ includeCache: [], componentCache: '', componentName: '', }), methods:{ changeClick(type) { if(type === 1) { if(!this.includeCache.includes('Demo1')) { this.includeCache.push('Demo1') } console.log(this.includeCache, '000') this.componentCache = Demo1 this.componentName = 'Demo1' } if(type === 2) { if(!this.includeCache.includes('Demo2')) { this.includeCache.push('Demo2') } console.log(this.includeCache, '2222') this.componentName = 'Demo2' this.componentCache = Demo2 } } } }).mount('#demo') </script>
7、偵錯原始碼發現keepalive中的render函數(或說時setup中的return 函數)在子元件切換時就會去執行,變更邏輯快取
#第一次進入頁面初始化keepalive元件會執行一次,
然後點選元件一,再執行render函數
然後點選元件二,會再執行render函數
8、偵錯截圖說明
5、vue3 keealive原始碼粗淺分析
透過查看vue3 KeepAlive.ts原始碼
// 在setup初始化中,先获取keepalive实例 // getCurrentInstance() 可以获取当前组件的实例 const instance = getCurrentInstance()! // KeepAlive communicates with the instantiated renderer via the // ctx where the renderer passes in its internals, // and the KeepAlive instance exposes activate/deactivate implementations. // The whole point of this is to avoid importing KeepAlive directly in the // renderer to facilitate tree-shaking. const sharedContext = instance.ctx as KeepAliveContext // if the internal renderer is not registered, it indicates that this is server-side rendering, // for KeepAlive, we just need to render its children /// SSR 判断,暂时可以忽略掉即可。 if (__SSR__ && !sharedContext.renderer) { return () => { const children = slots.default && slots.default() return children && children.length === 1 ? children[0] : children } } // 通过Map存储缓存vnode, // 通过Set存储缓存的key(在外面设置的key,或者vnode的type) const cache: Cache = new Map() const keys: Keys = new Set() let current: VNode | null = null if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { ;(instance as any).__v_cache = cache } const parentSuspense = instance.suspense const { renderer: { p: patch, m: move, um: _unmount, o: { createElement } } } = sharedContext // 创建了隐藏容器 const storageContainer = createElement('div') // 在实例上注册两个钩子函数 activate, deactivate sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => { const instance = vnode.component! move(vnode, container, anchor, MoveType.ENTER, parentSuspense) // in case props have changed patch( instance.vnode, vnode, container, anchor, instance, parentSuspense, isSVG, vnode.slotScopeIds, optimized ) queuePostRenderEffect(() => { instance.isDeactivated = false if (instance.a) { invokeArrayFns(instance.a) } const vnodeHook = vnode.props && vnode.props.onVnodeMounted if (vnodeHook) { invokeVNodeHook(vnodeHook, instance.parent, vnode) } }, parentSuspense) if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { // Update components tree devtoolsComponentAdded(instance) } } sharedContext.deactivate = (vnode: VNode) => { const instance = vnode.component! move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense) queuePostRenderEffect(() => { if (instance.da) { invokeArrayFns(instance.da) } const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted if (vnodeHook) { invokeVNodeHook(vnodeHook, instance.parent, vnode) } instance.isDeactivated = true }, parentSuspense) if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { // Update components tree devtoolsComponentAdded(instance) } } // 组件卸载 function unmount(vnode: VNode) { // reset the shapeFlag so it can be properly unmounted resetShapeFlag(vnode) _unmount(vnode, instance, parentSuspense, true) } // 定义 include和exclude变化时,对缓存进行动态处理 function pruneCache(filter?: (name: string) => boolean) { cache.forEach((vnode, key) => { const name = getComponentName(vnode.type as ConcreteComponent) if (name && (!filter || !filter(name))) { pruneCacheEntry(key) } }) } function pruneCacheEntry(key: CacheKey) { const cached = cache.get(key) as VNode if (!current || cached.type !== current.type) { unmount(cached) } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current) } 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)) }, // prune post-render after `current` has been updated { flush: 'post', deep: true } ) // 定义当前组件Key // cache sub tree after render let pendingCacheKey: CacheKey | null = null // 这是一个重要的方法,设置缓存 const cacheSubtree = () => { // fix #1621, the pendingCacheKey could be 0 if (pendingCacheKey != null) { cache.set(pendingCacheKey, getInnerChild(instance.subTree)) } } onMounted(cacheSubtree) onUpdated(cacheSubtree) // 组件卸载的时候,对缓存列表进行循环判断处理 onBeforeUnmount(() => { cache.forEach(cached => { const { subTree, suspense } = instance const vnode = getInnerChild(subTree) if (cached.type === vnode.type) { // current instance will be unmounted as part of keep-alive's unmount resetShapeFlag(vnode) // but invoke its deactivated hook here const da = vnode.component!.da da && queuePostRenderEffect(da, suspense) return } unmount(cached) }) }) // 同时在keepAlive组件setup生命周期中,return () => {} 渲染的时候,对组件进行判断逻辑处理,同样对include和exclude判断渲染。 // 判断keepalive组件中的子组件,如果大于1个的话,直接警告处理了 // 另外如果渲染的不是虚拟dom(vNode),则直接返回渲染即可。 return () => { // eslint-disable-next-line no-debugger console.log(props.include, 'watch-include') pendingCacheKey = null if (!slots.default) { return null } const children = slots.default() const rawVNode = children[0] if (children.length > 1) { if (__DEV__) { warn(`KeepAlive should contain exactly one component child.`) } current = null return children } else if ( !isVNode(rawVNode) || (!(rawVNode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) && !(rawVNode.shapeFlag & ShapeFlags.SUSPENSE)) ) { current = null return rawVNode } // 接下来处理时Vnode虚拟dom的情况,先获取vnode let vnode = getInnerChild(rawVNode) // 节点类型 const comp = vnode.type as ConcreteComponent // for async components, name check should be based in its loaded // inner component if available // 获取组件名称 const name = getComponentName( isAsyncWrapper(vnode) ? (vnode.type as ComponentOptions).__asyncResolved || {} : comp ) //这个算是最熟悉的通过props传递进行的参数,进行解构 const { include, exclude, max } = props // include判断 组件名称如果没有设置, 或者组件名称不在include中, // exclude判断 组件名称有了,或者匹配了 // 对以上两种情况都不进行缓存处理,直接返回当前vnode虚拟dom即可。 if ( (include && (!name || !matches(include, name))) || (exclude && name && matches(exclude, name)) ) { current = vnode return rawVNode } // 接下来开始处理有缓存或者要缓存的了 // 先获取一下vnode的key设置,然后看看cache缓存中是否存在 const key = vnode.key == null ? comp : vnode.key const cachedVNode = cache.get(key) // 这一段可以忽略了,好像时ssContent相关,暂时不管了,没看明白?? // clone vnode if it's reused because we are going to mutate it if (vnode.el) { vnode = cloneVNode(vnode) if (rawVNode.shapeFlag & ShapeFlags.SUSPENSE) { rawVNode.ssContent = vnode } } // 上面判断了,如果没有设置key,则使用vNode的type作为key值 pendingCacheKey = key //判断上面缓存中是否存在vNode // if 存在的话,就将缓存中的vnode复制给当前的vnode // 同时还判断了组件是否为过渡组件 transition,如果是的话 需要注册过渡组件的钩子 // 同时先删除key,然后再重新添加key // else 不存在的话,就添加到缓存即可 // 并且要判断一下max最大缓存的数量是否超过了,超过了,则通过淘汰LPR算法,删除最旧的一个缓存 // 最后又判断了一下是否为Suspense。也是vue3新增的高阶组件。 if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el vnode.component = cachedVNode.component if (vnode.transition) { // recursively update transition hooks on subTree setTransitionHooks(vnode, vnode.transition!) } // avoid vnode being mounted as fresh vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE // make this key the freshest keys.delete(key) keys.add(key) } else { keys.add(key) // prune oldest entry if (max && keys.size > parseInt(max as string, 10)) { pruneCacheEntry(keys.values().next().value) } } // avoid vnode being unmounted vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE current = vnode return isSuspense(rawVNode.type) ? rawVNode : vnode
以上是vue3 keepalive線上問題怎麼解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!

NetflixusesAcustomFrameworkcalled“ Gibbon” BuiltonReact,notReactorVuedIrectly.1)TeamSperience:selectBasedonFamiliarity.2)ProjectComplexity:vueforsimplerprojects:reactforforforproproject,reactforforforcompleplexones.3)cocatizationneedneeds:reactoffipicatizationneedneedneedneedneedneeds:reactoffersizationneedneedneedneedneeds:reactoffersizatization needefersmoreflexibleise.4)

Netflix在框架選擇上主要考慮性能、可擴展性、開發效率、生態系統、技術債務和維護成本。 1.性能與可擴展性:選擇Java和SpringBoot以高效處理海量數據和高並發請求。 2.開發效率與生態系統:使用React提升前端開發效率,利用其豐富的生態系統。 3.技術債務與維護成本:選擇Node.js構建微服務,降低維護成本和技術債務。

Netflix主要使用React作為前端框架,輔以Vue用於特定功能。 1)React的組件化和虛擬DOM提升了Netflix應用的性能和開發效率。 2)Vue在Netflix的內部工具和小型項目中應用,其靈活性和易用性是關鍵。

Vue.js是一種漸進式JavaScript框架,適用於構建複雜的用戶界面。 1)其核心概念包括響應式數據、組件化和虛擬DOM。 2)實際應用中,可以通過構建Todo應用和集成VueRouter來展示其功能。 3)調試時,建議使用VueDevtools和console.log。 4)性能優化可通過v-if/v-show、列表渲染優化和異步加載組件等實現。

Vue.js適合小型到中型項目,而React更適用於大型、複雜應用。 1.Vue.js的響應式系統通過依賴追踪自動更新DOM,易於管理數據變化。 2.React採用單向數據流,數據從父組件流向子組件,提供明確的數據流向和易於調試的結構。

Vue.js適合中小型項目和快速迭代,React適用於大型複雜應用。 1)Vue.js易於上手,適用於團隊經驗不足或項目規模較小的情況。 2)React的生態系統更豐富,適合有高性能需求和復雜功能需求的項目。

實現 Vue 中 a 標籤跳轉的方法包括:HTML 模板中使用 a 標籤指定 href 屬性。使用 Vue 路由的 router-link 組件。使用 JavaScript 的 this.$router.push() 方法。可通過 query 參數傳遞參數,並在 router 選項中配置路由以進行動態跳轉。

Vue 中實現組件跳轉有以下方法:使用 router-link 和 <router-view> 組件進行超鏈接跳轉,指定 :to 屬性為目標路徑。直接使用 <router-view> 組件顯示當前路由渲染的組件。使用 router.push() 和 router.replace() 方法進行程序化導航,前者保存歷史記錄,後者替換當前路由不留記錄。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。