前言在VUE项目中,有些组件或者页面没必要多次渲染,所以需要将部分组件有条件的在内存中进行"持久化",不过这里的持久化不是简单的数据持久化,而是整个组件(包括数据和视图)的持久化,刚好VUE提供了<keep-alive>这个内置组件来完成这件事情。<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。当组件在 <keep-alive> 内被切换,它的 activated和 deactivated 这两个生命周期钩子函数将会被对应执行。
基本使用使用的时候分两个版本。在vue 2.1.0 之前,大部分是这样实现的:
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 |
|
这样配置路由的路由元信息之后,a路由的$route.meta.keepAlive便为 true ,而b路由则为 false 。所以为 true 的将被包裹在 keep-alive 中,为 false 的则在外层。这样a路由便达到了被缓存的效果,如果还有想要缓存的路由,只需要在路由元中加入 keepAlive: true 即可。
在vue 2.1.0 版本之后,keep-alive 新加入了两个属性: include(包含的组件缓存生效) 与 exclude(排除的组件不缓存,优先级大于include) 。include 和 exclude 属性允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示。当使用正则或者是数组时,一定要使用 v-bind 。
简单使用:
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
推荐使用2.1.0以后的版本来做缓存策略,代码更加简洁,而且少了很多的重复渲染。
高级进阶使用我们了解了基本的使用,但是在日常项目中可没有想象的那么简单,所以需要设计一下整个项目的缓存策略,如何让所有组件可以动态的去切换自己的缓存属性是一个值得考虑的问题。
业务场景1.列表页进入详情页,详情页中有头也有行列表。
2.从详情页的行列表进入行详情页查看后返回详情页详情页不刷新,如果行详情页修改后,我进入详情页需要刷新。
这样的业务场景在移动端非常常见,当然解决方法也有很多,比如每次返回详情页传递给详情页一个标志之类的,然后在详情页做判断,根据标志去获取store里面的数据还是接口最新数据,这样做判断不仅麻烦,而且无法做到页面级的缓存,而且耗费内存资源。如果使用好了keep-alive,你可以轻松实现如上效果。
整体设计思路1.在store里面写三个方法:setKeepAlive,setNoKeepAlive,getKeepAlive,作用分别是设置需要缓存的组件,清除不需要缓存的组件,获取缓存的组件。
2.获得所有的组件的name属性,并将其存入store里面的一个数组中。
3.在App.vue挂载的时候去获取缓存的组件数组。
4.在页面路由拦截函数中去动态设置页面是不是要缓存。
5.在App.vue中监听store的变化,对include对应的数组进行赋值。
具体实现1.store里面注册三个方法
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
2.路由集合中去设置每个组件都缓存。
[JavaScript] 纯文本查看 复制代码
1 2 3 4 5 6 |
|
⚠️注意:这里没有写成store.commit('setKeepAlive', item.component.name),而是item.name。本应该写成store.commit('setKeepAlive', item.component.name),因为include接受的是组件的名,但是在按需加载的情况下打包后这个name会变化,所以在开始设计你的项目的时候尽量保证路由名和组件名一致。
3.在App.vue挂载的时候去获取缓存的组件数组,默认全部缓存。
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
4.动态改变页面的缓存属性。
比如我要从详情页跳转行详情了,跳转之前我不能让行详情有缓存,如果行详情有缓存的话,每次进去都是一样的。所以我要清除缓存。
">
我现在需要从行详情跳转到其他关联的单据了,那我肯定需要缓存一下行详情了,不然回到行详情啥都没有了。
">
5.监听缓存变化。
动态设置可以了,我现在还需要去动态绑定到include的数组上,所以我需要在每次页面跳转的时候去监听一下。
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
现在用法说完了,但是对其原理好像还不是很清楚,那就来看看他的实现方式。
原理解析keep-alive核心思想就是将组件缓存为vnode,然后用include里面的数组去匹配,匹配到就拿来直接用,如果exclude变化的话就销毁对应的vnode。
源码解析直接贴源码。大概理解写注释里面
[JavaScript] 纯文本查看 复制代码
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
|
简单总结1.created钩子会创建一个cache对象,用来作为缓存容器,保存vnode节点。destroyed钩子则在组件被销毁的时候清除cache缓存中的所有组件实例。
2.在render函数中主要做了这些事情:
• 首先通过getFirstComponentChild获取第一个子组件,获取该组件的name(存在组件名则直接使用组件名,否则会使用tag)。
• 接下来会将这个name通过include与exclude属性进行匹配,匹配不成功(说明不需要进行缓存)则不进行任何操作直接返回vnode,vnode是一个VNode类型的对象。
• include与exclude属性支持字符串如"a,b,c"这样组件名以逗号隔开的情况以及正则表达式。matches通过这两种方式分别检测是否匹配当前组件。
• 然后根据key在this.cache中查找,如果存在则说明之前已经缓存过了,直接将缓存的vnode的componentInstance(组件实例)覆盖到目前的vnode上面,否则将vnode存储在cache中。最后返回vnode(有缓存时该vnode的componentInstance已经被替换成缓存中的了)。
3.需要监听改变就用watch来监听pruneCache与pruneCache这两个属性的改变,在改变的时候修改cache缓存中的缓存数据。
4.Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。
文章转载自:https://juejin.im/post/5eb143676fb9a0437d2b2275