keep-alive如何保持组件状态(下)

简介: keep-alive的设计初衷: 有些业务场景需要根据不同的判断条件,动态地在多个组件之间切换。频繁的组件切换会导致组件反复渲染,如果组件包含有大量的逻辑和dom节点,极易造成性能问题。其次,切换后组件的状态也会完全丢失。keep-alive的设计初衷就是为了保持组件的状态,避免组件的重复渲染。
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
      // 1、如果缓存中存在该vnode,从缓存中取得该组件的实例(一个组件对应一颗vnode树,同时一个组件对应一个vue子类的实例),不再重新创建
      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        // 将当前的组件的key作为最新的缓存(更新其在keys数组中的顺序)
        remove(keys, key)
        keys.push(key)
      } else {
        // 2、如果未命中缓存,添加到缓存
        cache[key] = vnode
        keys.push(key)
        // 如果缓存超过限制,淘汰最旧的缓存
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }
      // 标记为keepAlive组件
      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }


keep-alive内部就是单独提供了render函数来自定义了vnode的创建逻辑。首先keep-alive获取到其所包裹的子组件的根vnode,然后去cache中查找该组件是否存在。


如果cache中不存在子组件vnode,则以{子组件名: 子组件vnode}的形式保存到cache对象中。同时将子组件名字保存到keys数组中。同时如果当前缓存的数量已经超过max所设置的最大值,需要淘汰掉最近最少使用的缓存项(LRU)。


如果cache中存在子组件vnode,那么只需要复用缓存的组件vnode的组件实例(componentInstance)。同时需要将该子组件vnode在缓存中顺序调到最前面,这个主要是为了在缓存不足时,正确地淘汰缓存项。


举例说明


最后通过一个例子加深一下理解。


<div id="app">
    <keep-alive><component :is="view"></component></keep-alive>
    <button @click="view = view =='count'? 'any': 'count'">切换组件</button>
</div>


Vue.component("count", {
    data() {
        return {
            count:0
        };
    },
    template: "<div @click='count+=1'>点了我 {{count}} 次</div>"
});
Vue.component("any", {
    template: "<div>any</div>"
});
new Vue({
    el: "#app",
    data: {
      view: "count"
    }
});


由于view默认值是count,因此keep-alive包裹的子组件是count。此时keep-alive的缓存中为空,因此会把组件count的vnode添加到缓存。缓存结果为:


cache = {1::count: {tag: "vue-component-1-count", data:{tag: "component", hook: {…}}}, componentOptions, componentInstance, ...}
keys = ["1::count"]


页面显示结果为:


网络异常,图片无法展示
|


点击一下组件count,组件的显示内容变成"点了我1次",然后切换到组件any。与count组件相同,由于在keep-alive的缓存中还未保存any组件的vnode,因此需要将any添加到缓存中。此时缓存结果变成了:


cache = {
    1::count: {tag: "vue-component-1-count", data:{tag: "component", hook: {…}}, componentOptions, componentInstance, ...},
    2::any: {tag: "vue-component-2-any", data:{tag: "component", hook: {…}}, componentOptions, componentInstance, ...},
}
keys = ["1::count", "2::any"]


页面显示结果为:


网络异常,图片无法展示
|


再次点击切换组件,切回count。此时count组件的vnode在缓存中已经存在,所以直接复用了原来count组件vnode中所保存的组件实例,组件实例中保存了原来的count值,因此组件切换完后,组件的状态也跟着还原了回来。


下图为count组件实例的状态,可以看到count的值得到了保持:


网络异常,图片无法展示
|


最终页面显示结果为:


网络异常,图片无法展示
|


从上面的分析可知,如果组件被包裹在keep-alive组件中,组件vnode会缓存到cache中。而组件的状态又会保存在组件实例中(componentInstance),当组件再次切换回来时,keep-alive直接将之前缓存的状态进行还原,也就实现了组件状态的保持。


相关文章
|
6月前
|
存储 缓存 JavaScript
keep-alive的两个属性的作用
keep-alive的两个属性的作用
148 0
|
6月前
|
存储 缓存 JavaScript
缓存组件状态,提升用户体验:探索 keep-alive 的神奇世界
缓存组件状态,提升用户体验:探索 keep-alive 的神奇世界
缓存组件状态,提升用户体验:探索 keep-alive 的神奇世界
|
6月前
|
存储 缓存 JavaScript
Uservue 中 keep-alive 组件的作用
Uservue 中 keep-alive 组件的作用
66 0
|
28天前
|
缓存 UED
动态组件与 keep-alive 搭配使用时的生命周期钩子
【10月更文挑战第19天】动态组件与 keep-alive 搭配使用时的生命周期钩子为我们提供了更多的灵活性和可操作性,使我们能够更好地管理组件的状态和行为。深入理解和掌握这些钩子的特点和用法,以便在实际开发中能够更加得心应手地运用它们,为我们的应用带来更优秀的用户体验和性能表现。
112 62
|
1月前
|
缓存 JavaScript UED
|
1月前
|
API
Harmony 状态监听 @Monitor和@Computed
Harmony 状态监听 @Monitor和@Computed
51 0
|
3月前
|
前端开发 JavaScript 算法
React Server Component 使用问题之想在路由切换时保持客户端状态,如何实现
React Server Component 使用问题之想在路由切换时保持客户端状态,如何实现
|
5月前
|
缓存 监控 JavaScript
使用 keep-alive 时,监控和分析组件的缓存行为
使用 keep-alive 时,监控和分析组件的缓存行为
|
5月前
|
缓存
keep-alive 组件有哪些常用的属性和配置选项
keep-alive 组件有哪些常用的属性和配置选项