zoukankan      html  css  js  c++  java
  • vue keep-alive的实现原理和缓存策略

    使用

    <!-- 基本 -->
    <keep-alive>
      <component :is="view"></component>
    </keep-alive>
    

    <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。

    当组件在 <keep-alive> 内被切换,它的 activateddeactivated 这两个生命周期钩子函数将会被对应执行。

    原理

    /* keep-alive组件 */
    export default {
      name: 'keep-alive',
      /* 抽象组件 */
      abstract: true,
    
      props: {
        include: patternTypes,
        exclude: patternTypes
      },
    
      created () {
        /* 缓存对象 */
        this.cache = Object.create(null)
      },
    
      /* destroyed钩子中销毁所有cache中的组件实例 */
      destroyed () {
        for (const key in this.cache) {
          pruneCacheEntry(this.cache[key])
        }
      },
    
      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))
        }
      },
    
      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
            // 2.5.0 新增这段逻辑,使用 LRU 策略 make current key freshest
            remove(keys, key);
            keys.push(key);
          }
          // 不命中缓存,把 vnode 设置进缓存
          else {
            this.cache[key] = vnode;
            // 2.5.0 新增这段逻辑,LRU 策略的移除。
            keys.push(key);
            // 如果配置了 max 并且缓存的长度超过了 this.max,还要从缓存中删除第一个
            if (this.max && keys.length > parseInt(this.max)) {
                pruneCacheEntry(cache, keys[0], keys, this._vnode);
            }
            
          }
          /* keepAlive标记位 */
          vnode.data.keepAlive = true
        }
        return vnode
      }
    }
    
    
    1. 获取 keep-alive 包裹着的第一个子组件对象及其组件名
      根据设定的 include/exclude(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例
    2. 根据组件 ID 和 tag 生成缓存 Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该 key 在 this.keys 中的位置(更新 key 的位置是实现 LRU 置换策略的关键)
    3. 在 this.cache 对象中存储该组件实例并保存 key 值,之后检查缓存的实例数量是否超过 max 的设置值,超过则根据 LRU 置换策略删除最近最久未使用的实例(即是下标为 0 的那个 key)
    4. 最后组件实例的 keepAlive 属性设置为 true,这个在渲染和执行被包裹组件的钩子函数会用到,

    LRU 策略

    最近最久未使用算法。

    每次从内存中找出最久的未使用数据用于置换新的数据。

  • 相关阅读:
    前端开发中同步和异步的区别
    SQL STUFF函数 拼接字符串
    Javascript的精华
    .net网站报错:对象的当前状态使该操作无效
    免费 WebOffice使用
    DotNetBar教程
    C#获取周的第一天、最后一天、月第一天和最后一天
    C#判断文字是否为汉字
    C# 将字符串转为&#2345;这种的 html实体编码
    SqlServer将没有log文件的数据库文件附加到服务器中
  • 原文地址:https://www.cnblogs.com/everlose/p/12541934.html
Copyright © 2011-2022 走看看