Vue中keep-alive组件的理解
对keep-alive组件的理解
当在组件之间切换的时候,有时会想保持这些组件的状态,以避免反复重渲染导致的性能等问题,使用<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
描述
重新创建动态组件的行为通常是非常有用的,但是在有些情况下我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来,此时使用<keep-alive>
包裹组件即可缓存当前组件实例,将组件缓存到内存,用于保留组件状态或避免重新渲染,和<transition>
相似它,其自身不会渲染一个DOM
元素,也不会出现在组件的父组件链中。
<keep-alive>
<component v-bind:is="currentComponent" class="tab"></component>
</keep-alive>
被<keep-alive>
包含的组件不会被再次初始化,也就意味着不会重走生命周期函数,<keep-alive>
保持了当前的组件的状态,在第一次创建的时候回正常触发其创建生命周期,但是由于组件其实并未销毁,所以不会触发组件的销毁生命周期,而当组件在<keep-alive>
内被切换时,它的activated
和deactivated
这两个生命周期钩子函数将会被对应执行。
export default {
data: function() {
return {
}
},
activated: function(){
console.log("activated");
},
deactivated: function(){
console.log("deactivated");
},
}
<keep-alive>
可以接收3
个属性做为参数进行匹配对应的组件进行缓存,匹配首先检查组件自身的name
选项,如果name
选项不可用,则匹配它的局部注册名称,即父组件components
选项的键值,匿名组件不能被匹配,除了使用<keep-alive>
的props
控制组件缓存,通常还可以配合vue-router
在定义时的meta
属性以及在template
定义的<keep-alive>
进行组件的有条件的缓存控制。
include
: 包含的组件,可以为字符串,数组,以及正则表达式,只有匹配的组件会被缓存。exclude
: 排除的组件,以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存,当匹配条件同时在include
与exclude
存在时,以exclude
优先级最高。max
: 缓存组件的最大值,类型为字符或者数字,可以控制缓存组件的个数,一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
<!-- 包含 逗号分隔字符串 -->
<keep-alive include="a,b">
<component :is="show"></component>
</keep-alive>
<!-- 包含 正则表达式 使用v-bind -->
<keep-alive :include="/a|b/">
<component :is="show"></component>
</keep-alive>
<!-- 包含 数组 使用v-bind -->
<keep-alive :include="['a', 'b']">
<component :is="show"></component>
</keep-alive>
<!-- 排除 逗号分隔字符串 -->
<keep-alive exclude="a,b">
<component :is="show"></component>
</keep-alive>
<!-- 最大缓存量 数字 -->
<keep-alive :max="10">
<component :is="show"></component>
</keep-alive>
<keep-alive>
是用在其一个直属的子组件被开关的情形,如果在其中有v-for
则不会工作,如果有上述的多个条件性的子元素,<keep-alive>
要求同时只有一个子元素被渲染,通俗点说,<keep-alive>
最多同时只能存在一个子组件,在<keep-alive>
的render
函数中定义的是在渲染<keep-alive>
内的组件时,Vue
是取其第一个直属子组件来进行缓存。
const vnode: VNode = getFirstComponentChild(this.$slots.default);
实现
Vue
中<keep-alive>
组件源码定义在dev/src/core/components/keep-alive.js
,本次分析实现的commit id
为215f877
。
在<keep-alive>
初始化时的created
阶段会初始化两个变量,分别为cache
和keys
,mounted
阶段会对include
和exclude
变量的值做监测。
export default {
created () {
this.cache = Object.create(null)
this.keys = []
},
mounted () {
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
}
上边的$watch
方法能够对参数的变化进行检测,如果include
或者exclude
的值发生变化,就会触发pruneCache
函数,不过筛选的条件需要根据matches
函数的返回值来决定,matches
函数接收三种类型的参数string
、RegExp
、Array
,用以决定是否进行缓存。
function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
if (Array.isArray(pattern)) {
return pattern.indexOf(name) > -1
} else if (typeof pattern === 'string') {
return pattern.split(',').indexOf(name) > -1
} else if (isRegExp(pattern)) {
return pattern.test(name)
}
/* istanbul ignore next */
return false
}
pruneCache
函数用以修建不符合条件的key
值,每当过滤条件改变,都需要调用pruneCacheEntry
方法从已有的缓存中修建不符合条件的key
。
function pruneCache (keepAliveInstance: any, filter: Function) {
const { cache, keys, _vnode } = keepAliveInstance
for (const key in cache) {
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
if (name && !filter(name)) {
pruneCacheEntry(cache, key, keys, _vnode)
}
}
}
}
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>,
current?: VNode
) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
cached.componentInstance.$destroy()
}
cache[key] = null
remove(keys, key)
}
在每次渲染即render
时,首先获取第一个子组件,之后便是获取子组件的配置信息,获取其信息,判断该组件在渲染之前是否符合过滤条件,不需要缓存的便直接返回该组件,符合条件的直接将该组件实例从缓存中取出,并调整该组件在keys
数组中的位置,将其放置于最后,如果缓存中没有该组件,那么将其加入缓存,并且定义了max
并且缓存组件数量如果超出max
定义的值则将第一个缓存的vnode
移除,之后返回组件并渲染。
export default {
render () {
const slot = this.$slots.default
const vnode: VNode = getFirstComponentChild(slot)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
const name: ?string = getComponentName(componentOptions)
const { include, exclude } = this
if (
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode
}
const { cache, keys } = this
const key: ?string = vnode.key == null
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
} else {
cache[key] = vnode
keys.push(key)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
}
每日一题
https://github.com/WindrunnerMax/EveryDay
参考
https://zhuanlan.zhihu.com/p/85120544
https://cn.vuejs.org/v2/api/#keep-alive
https://juejin.im/post/6844904082038063111
https://juejin.im/post/6844903919273918477
https://juejin.im/post/6844904099272458253
https://juejin.im/post/6844904160962281479
https://fullstackbb.com/vue/deep-into-keep-alive-in-vuejs/
https://blog.liuyunzhuge.com/2020/03/20/%E7%90%86%E8%A7%A3vue%E4%B8%ADkeep-alive%E7%BB%84%E4%BB%B6%E6%BA%90%E7%A0%81/
Vue中keep-alive组件的理解的更多相关文章
- 【Vue】Vue中的父子组件通讯以及使用sync同步父子组件数据
前言: 之前写过一篇文章<在不同场景下Vue组件间的数据交流>,但现在来看,其中关于“父子组件通信”的介绍仍有诸多缺漏或者不当之处, 正好这几天学习了关于用sync修饰符做父子组件数据双向 ...
- vue中的父子组件相互调用
vue中的父子组件相互调用: 1.vue子组件调用父组件方法:子组件:this.$emit('xx'); 父组件:定义yy方法,并在引用子组件时传参,如@xx="yy" 2.vue ...
- vue中修改子组件样式
一.问题叙述 项目里需要新添加一个表单页面,里面就只是几个select,这个几个select是原本封装好的组件,有自己原本的样式,而这次的原型图却没有和之前的样式统一起来,需要微调一下,这里就涉及到父 ...
- vue中8种组件通信方式, 值得收藏!
vue是数据驱动视图更新的框架, 所以对于vue来说组件间的数据通信非常重要,那么组件之间如何进行数据通信的呢? 首先我们需要知道在vue中组件之间存在什么样的关系, 才更容易理解他们的通信方式, 就 ...
- vue中使用keepAlive组件缓存遇到的坑
项目开发中在用户由分类页category进入detail需保存用户状态,查阅了Vue官网后,发现vue2.0提供了一个keep-alive组件. 上一篇讲了keep-alive的基本用法,现在说说遇到 ...
- Vue中,父组件向子组件传值
1:在src/components/child/文件夹下,创建一个名为:child.vue的子组件 2:在父组件中,设置好需要传递的数据 3:在App.vue中引入并注册子组件 4:通过v-bind属 ...
- vue中兄弟之间组件通信
我们知道Vue中组件之间的通信有很多方式,父子之间通信比较简单,当我们使用vuex时候,兄弟组件之间的通信也很好得到解决 当我们项目较小时候,不使用vuex时候Vue中兄弟组件之间的通信是怎样进行的呢 ...
- Vue中iframe和组件的通信
最近的项目开发中用到了Vue组件中嵌套iframe,相应的碰到了组件和HTML的通信问题,场景如下:demo.vue中嵌入 test.html 由于一般的iframe嵌套是用于HTML文件的,在vue ...
- 在vue中使用swiper组件
第一步:在终端的项目根目录下载安装swiper: cnpm/npm install vue-awesome-swiper --save; 第二步:在程序入口文件main.js中引用: import V ...
- vue中修改第三方组件的样式并不造成污染
vue引用了第三方组件, 需要在组件中局部修改第三方组件的样式, 而又不想去除scoped属性造成组件之间的样式污染. 此时只能通过>>>,穿透scoped. 但是,在sass中存在 ...
随机推荐
- LQB2018A09倍数问题
这个题,第一反应一定是三个for嵌套加一个max比较. 超级无敌大暴搜 #include <iostream> #include <string> #include <s ...
- 如何使用k3OS和Argo进行自动化边缘部署?
本文转自边缘计算k3s社区 前 言 随着Kubernetes生态系统的发展,新的技术正在被开发出来,以实现更广泛的应用和用例.边缘计算的发展推动了对其中一些技术的需求,以实现将Kubernetes部署 ...
- 汇编语言从键盘输入一个字符串(串长不大于80)以十进制输出字符串中非字母字符的个数(不是a to z或 A to Z)
(1)从键盘输入一个字符串(串长不大于80). (2)以十进制输出字符串中非字母字符的个数(不是a to z或 A to Z). (3)输出原字符串且令非字母字符闪烁显示. (4)找出字符串中ASCI ...
- PHP xml_get_current_column_number() 函数
定义和用法 xml_get_current_column_number() 函数获取 XML 解析器的当前列号. 如果成功,该函数则返回当前列号.如果失败,则返回 FALSE.高佣联盟 www.cge ...
- Prism.Interactivity 和 Prism.Modularity 介绍
Prism.Interactivity: 主要用来截取View即界面的一些处理,而这些功能通过vm 不好实现,只能用 CommandBehaviorBase 来截取处理,特别是在处理界面异常很有用. ...
- UOJ #310 黎明前的巧克力 FWT dp
LINK:黎明前的巧克力 我发现 很多难的FWT的题 都和方程有关. 上次那个西行寺无余涅槃 也是各种解方程...(不过这个题至今还未理解. 考虑dp 容易想到f[i][j][k]表示 第一个人得到巧 ...
- windows:根据特征码查找内核任意函数
在windows平台做逆向.外挂等,经常需要调用很多未导出的内核函数,怎么方便.快速查找了?可以先用IDA等工具查看硬编码,再根据硬编码定位到需要调用的函数.整个思路大致如下: 1.先查找目标模块 ...
- 我靠!Semaphore里面居然有这么一个大坑!
这是why的第 59 篇原创文章 荒腔走板 大家好,我是why哥 ,欢迎来到我连续周更优质原创文章的第 59 篇. 上周写了一篇文章,一不小心戳到了大家的爽点,其中一个转载我文章的大号,阅读量居然突破 ...
- JAVA编程中你一定要掌握的“快捷键”
JAVA编程常用快捷键 相信很多编程小白刚开始的时候,看向大神的时候都是双膝跪地满眼泪水的膜拜之情~不因为别的,就是因为他们可以随随便便敲出很多行代码,而且他们没有动鼠标!这时候就有人问了:“怎么才能 ...
- cocos2d-x_下载游戏引擎并创建第一个项目
我是一名小白. 下载并创建游戏项目 第一步:去官网下载cocos2d-x http://www.cocos.com/download 第二步:将安装包里边的 setup.py 拖进命令行点击回车键 , ...