Vue高级用法之keep-live

1,582 阅读2分钟

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

<div id="app">
    <button @click="changeTabs('child1')">child1</button>
    <button @click="changeTabs('child2')">child2</button>
    <keep-alive>
        <component :is="chooseTabs">
        </component>
    </keep-alive>
</div>

keep-alive的使用需要在动态组件的最外层添加标签。

keep-alive 原理

在具体实现上,keep-alive在内部维护了一个key数组和一个缓存对象。

//keep-alive 内部声明周期函数
  created () {
    this.cache = Object.create(null)
    this.keys = []
  },

key数组记录目前缓存的组件key值,如果组件没有指定key值,会自动生成一个唯一的key值

cache对象会以key值为键,vnode为值,用于缓存组件对应的虚拟DOM

在keep-alive的渲染函数中,其基本逻辑是判断当前渲染的vnode是否有对应的缓存,如果有,会从缓存中读取到对应的组件实例,如果没有就会把它缓存。

当缓存的数量超过max设置的数值时,keep-alive会移除key数组中的第一个元素

 render () {
    const slot = this.$slots.default; //获取默认插槽
    const vnode = getFirstComponentChild(slot); //得到插槽中第一个组件的vnode
    const name = getComponentName(vnode.componentOptions); //获取组件名字
   
    const { cache, keys } = this; //获取当前的混村内对象和key数组
    const key: ?string = vnode.key == null
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key; //获取组件的key值,如果没有key值,会按照规则自动生成
      if (cache[key]) {
      //有缓存
      //重用组件实例
        vnode.componentInstance = cache[key].componentInstance    
        remove(keys, key); //删除key值
        //将key值加入到数组末尾,这样是为了保证最近使用的组件在数组中靠后,主要是为了方便设置的max值删除很久没使用的组件
        keys.push(key)
      } else {
      //没有缓存的则进行缓存
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
        //超过最大缓存数量,移除第一个key对应的缓存
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }

总结

不管是内置的还是用户定义组件,本质上组件在模板编译成render函数的处理方式是一致的,这里的细节不展开分析。最终针对keep-aliverender函数的结果如下:

with(this){···_c('keep-alive',{attrs:{"include":"child2"}},
[_c(chooseTabs,{tag:"component"})],1)}

有了render函数,接下来从子开始到父会执行生成Vnode对象的过程,_c('keep-alive'···)的处理,会执行createElement生成组件Vnode,其中由于keep-alive是组件,所以会调用createComponent函数去创建子组件Vnode,createComponent之前也有分析过,这个环节和创建普通组件Vnode不同之处在于,keep-aliveVnode会剔除多余的属性内容,由于keep-alive除了slot属性之外,其他属性在组件内部并没有意义,例如class样式,<keep-alive clas="test"></keep-alive>等,所以在Vnode层剔除掉多余的属性是有意义的。而<keep-alive slot="test">的写法在2.6以上的版本也已经被废弃。