您现在的位置是:网站首页> 编程资料编程资料
vue3 keepalive源码解析解决线上问题_vue.js_
2023-05-24
474人已围观
简介 vue3 keepalive源码解析解决线上问题_vue.js_
引言
- 1、通过本文可以了解到vue3 keepalive功能
- 2、通过本文可以了解到vue3 keepalive使用场景
- 3、通过本文可以学习到vue3 keepalive真实的使用过程
- 4、通过本文可以学习vue3 keepalive源码调试
- 5、通过本文可以学习到vue3 keepalive源码的精简分析
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组件,通过这里可以发现,我这里缓存的相当于整个页面,当然你也可以进行更细粒度的控制页面当中的某个区域组件
通过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) } }); } } 组件路由离开时判断,是否要移出缓存,这个钩子就直接写在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.jb51.net/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便可以找到这个组件,然后通过左侧行标右键就可以添加断点,进行调试,也可以通过右侧的【调用堆栈】进行快速跳转代码进行调试。
Hello WorldHello WorldHello World
7、调试源码发现 keepalive中的render函数(或者说时setup中的return 函数)在子组件切换时就会去执行,变更逻辑缓存
- 第一次进入页面初始化keepalive组件会执行一次,
- 然后点击组件一,再次执行render函数
- 然后点击组件二,会再次执行render函数
8、调试截图说明

9、调试操作,小视频观看

5、vue3 keealive源码粗浅分析
通过查看vue3 KeepAlive.ts源码,源码路径:https://github.com/vuejs/core/blob/main/packages/runtime-core/src/components/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 { rendere
相关内容
- 微信小程序原生自定义弹窗效果_javascript技巧_
- vue+elementUi实现点击地图自动填充经纬度以及地点_vue.js_
- Node.js数据流Stream之Readable流和Writable流用法_node.js_
- vue+elementui实现动态添加行/可编辑的table_vue.js_
- typescript+vite项目配置别名的方法实现_vue.js_
- Typescript中使用引用路径别名报错的解决方法_javascript技巧_
- typescript在node.js下使用别名(paths)无效的问题详解_javascript技巧_
- iview+vue实现导入EXCEL预览功能_vue.js_
- Node.js处理I/O数据之使用Buffer模块缓冲数据_node.js_
- vue实现页面添加水印_vue.js_
点击排行
本栏推荐
