keep-alive
keep-alive是vue.js的内置组件,它可以把不运动的组件的实例保留在内存中,而不是直接的烧毁,它是一个笼统组件,不会被衬着到实在DOM中,也不会出现在父组件链中。
它供应了exclude和include两个属性,许可组件有前提的缓存。
运用
<keep-alive>
<comment></comment>
</keep-alive>
上面的comment组件会被缓存起来。
<keep-alive>
<coma v-if="test"></coma>
<comb v-else></comb>
</keep-alive>
<button @click="abc"></button>
export default{
data(){
reurn{
test:true
}
},
methods:{
abc(){
this.test=!this.test;
}
}
}
点击button的时刻coma组件和comb组件会发作切换,但这时候刻两个组件的状况会被缓存起来,假如说a和b组件中都有一个input标签,这时候切换input标签的值不会转变。
props
keep-alive组件供应了include和exclude两个属性来举行有前提的缓存,两者都可以用逗号分开字符串、正则表达式或则数组示意。
<keep-alive include="a">
<component></component>
</keep-alive>
//name名为a的组件会被缓存起来
<keep-alive exclude="a">
<component></component>
</keep-alive>
//name名为a的组件将不会被缓存。
性命钩子
keep-alive供应了两个性命钩子,actived与deactived。
由于keep-alive会把组件保留到内存中,并不会烧毁或则从新构建,所以不会挪用组件的creted等要领,须要运用actived和deactived两个钩子推断组件是不是处于运动状况。
深切keep-alive组件的完成
created和destroyed钩子
created钩子会建立一个cache对象,用来作为缓存容器,保留Vnode节点。
created{
this.cache=Object.create(null);
}
destroyed钩子则在组件烧毁的时刻消灭cache缓存中的一切组件实例。
/* destroyed钩子中烧毁一切cache中的组件实例 */
destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache[key])
}
},
接下来是render函数。
render () {
/* 获得slot插槽中的第一个组件 */
const vnode: VNode = getFirstComponentChild(this.$slots.default)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
/* 猎取组件称号,优先猎取组件的name字段,不然是组件的tag */
const name: ?string = getComponentName(componentOptions)
/* name不在inlcude中或许在exlude中则直接返回vnode(没有取缓存) */
if (name && (
(this.include && !matches(this.include, name)) ||
(this.exclude && matches(this.exclude, name))
)) {
return vnode
}
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
/* 假如已做过缓存了则直接从缓存中猎取组件实例给vnode,还未缓存过则举行缓存 */
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
} else {
this.cache[key] = vnode
}
/* keepAlive标记位 */
vnode.data.keepAlive = true
}
return vnode
}
起首经由过程getFirstComponentChild猎取第一个子组件,猎取该组件的name(存在组件名则直接运用组件名,不然会运用tag)。接下来会将这个name经由过程include与exclude属性举行婚配,婚配不成功(申明不须要举行缓存)则不举行任何操纵直接返回vnode。
/* 检测name是不是婚配 */
function matches (pattern: string | RegExp, name: string): boolean {
if (typeof pattern === 'string') {
/* 字符串状况,如a,b,c */
return pattern.split(',').indexOf(name) > -1
} else if (isRegExp(pattern)) {
/* 正则 */
return pattern.test(name)
}
/* istanbul ignore next */
return false
}
检测include与exclude属性婚配的函数很简单,include与exclude属性支撑字符串如”a,b,c”如许组件名以逗号离隔的状况以及正则表达式。matches经由过程这两种体式格局离别检测是不是婚配当前组件。
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
} else {
this.cache[key] = vnode
}
接下来的事变很简单,依据key在this.cache中查找,假如存在则申明之前已缓存过了,直接将缓存的vnode的componentInstance(组件实例)掩盖到现在的vnode上面。不然将vnode存储在cache中。
末了返回vnode(有缓存时该vnode的componentInstance已被替换成缓存中的了)。
用watch来监听pruneCache与pruneCache这两个属性的转变,在转变的时刻修改cache缓存中的缓存数据。
watch: {
/* 看管include以及exclude,在被修改的时刻对cache举行修改 */
include (val: string | RegExp) {
pruneCache(this.cache, this._vnode, name => matches(val, name))
},
exclude (val: string | RegExp) {
pruneCache(this.cache, this._vnode, name => !matches(val, name))
}
},
来看一下pruneCache的完成。
/* 修改cache */
function pruneCache (cache: VNodeCache, current: VNode, filter: Function) {
for (const key in cache) {
/* 掏出cache中的vnode */
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
/* name不符合filter前提的,同时不是现在衬着的vnode时,烧毁vnode对应的组件实例(Vue实例),并从cache中移除 */
if (name && !filter(name)) {
if (cachedNode !== current) {
pruneCacheEntry(cachedNode)
}
cache[key] = null
}
}
}
}
/* 烧毁vnode对应的组件实例(Vue实例) */
function pruneCacheEntry (vnode: ?VNode) {
if (vnode) {
vnode.componentInstance.$destroy()
}
}
遍历cache中的一切项,假如不符合filter指定的划定规矩的话,则会实行pruneCacheEntry。pruneCacheEntry则会挪用组件实例的$destroy要领来将组件烧毁。