vue2中值得关注的全局Api之keep-alive

lxf2023-05-02 19:48:23

本系列

-vue2中值得关注的全局Api之config对象

-vue2中值得关注的全局Api之util对象

Vue2中这些全局Api很重要,谈到源码大家都关注响应原理模板编译更新策略这些点,但是Vue在第一步做的事情是注册全局Api,没有全局Api就没有后面的一切

全局Api都在initGlobalAPI函数中初始化

function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  Object.defineProperty(Vue, 'config', configDef)

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)

  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}

ASSET_TYPES中有'component', 'directive', 'filter' 这三个变量,通过循环添加上options中静态变量'components', 'directives', 'filters'

keep-alive

extend(Vue.options.components, builtInComponents)

components属性通过extend方法赋值,Vue.util.extend方法就是简单的浅拷贝,用后者覆盖前者相同key的值对象

这里builtInComponents只有keep-alive内部组件

基本用法 include、exclude、max 参数作用

看源码

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 {
        // delay setting the cache until update
        this.vnodeToCache = vnode
        this.keyToCache = key
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
  • getFirstComponentChild 获取默认default插槽的第一个子组件,所以如果有多个子组件只取第一个子组件,其他都废弃

  • 组件名name是怎么得来的,根据getComponentName先判断name属性,没有就取标签名

  • 如果有include 或者 exclude 参数,那么根据name判断,如果在include就缓存,或者在exclue就不缓存

  • 如果没有include 和 exclude 那就直接要检查缓存

  • 如果cache[key]存在,则从cacel中获取 componentInstance 对象给vnode,并刷新key在数组中的顺序

  • key是什么呢,如果vnode有key就直接用,如果没有就是拼接cid和标签名的字符串,cid很重要,如果是同一组件多次出现,这cid是不同的

  • 如果是第一次进来,则会在mounted和updated两个钩子中,把vnode放入数组,在render函数中只是设置vnodeToCache,真正在mounted和updated中才去设置cache[keyToCache]

// delay setting the cache until update
this.vnodeToCache = vnode

为什么不这里直接压入缓存呢,因为componentInstance对象就是子组件实例this,还没有生成此时是null

  • max的作用是设置最大缓存对象个数

这就是实际缓存的函数,keys.push最新的key,如果是updated执行,也会把key从旧位置放到队列最后,下面pruneCacheEntry检查到max超了,就去掉第一个key,也就是LRU策略,最新的在最后面,超出后第一个就去掉

cacheVNode() {
  const { cache, keys, vnodeToCache, keyToCache } = this
  if (vnodeToCache) {
    const { tag, componentInstance, componentOptions } = vnodeToCache
    cache[keyToCache] = {
      name: getComponentName(componentOptions),
      tag,
      componentInstance,
    }
    keys.push(keyToCache)
    // prune oldest entry
    if (this.max && keys.length > parseInt(this.max)) {
      pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
    this.vnodeToCache = null
  }
}