天天看点

Vue源码学习之keep-alive原理

大部分的Vue应用都采用spa模式开发,从而达到一次加载,全站使用的目的,可以极大地提高应用的用户体验。而spa应用的原理其实就是动态的把目标页面的代码替换到路由组件内,从而实现路由切换的目的。那么,问题来了,加入我有页面A、页面B两个页面,页面A中有一堆表单,用户填了一半,然后切换到B页面去看一个消息,看完想要回到页面A继续填写表单,如果不经任何处理的话,用户回到页面A的结果只有一个,自己辛辛苦苦填了老半天的表单数据都被清空了,又得重新填写。

显然,这是一个极差的用户体验,不过别担心,Vue已经为我们考虑了这个问题,它已经为我们提供了一个内置组件keep-alive组件,只要按照要求使用keep-alive组件,当你从页面A切换到页面B时,keep-alive会帮你吧页面A已经输入的一些数据缓存下来,当你再从页面B回到页面A时,表单的数据便可以自动回填,从而让用户可以继续自己未完成的表单填写。

那么,keep-alive组件究竟是如何工作的呢?我们来一起看看吧。

节选自Vue源码中的src/core/components/keep-alive.js

export default {
  name: 'keep-alive',
  abstract: true,

  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },

  created () {
    this.cache = Object.create(null)
    this.keys = []
  },

  destroyed () {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },

  mounted () {
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  },

  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 {
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }

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

从Vue源码可以看出,当页面组件触发了destroy生命周期时,keep-alive会将当前组件缓存到一个缓存对象当中,然后才进行页面跳转,当页面重新切换回页面A时,keep-alive会先看看缓存中是否存在当前组件的缓存数据,如果存在,便会把数据重新回填至该组件中,由于页面上的表单都采用双向绑定,组件中的初始值被填充为缓存的值时,表单自然也会恢复回页面跳转出去前的样子,这样就实现了让页面组件缓存与恢复缓存数据的逻辑了

PS:以上为本人在学习Vue源码时个人的学习感想与记录,如有不妥,欢迎指正。