Keep-alive is a built-in component of vue.js. <keep-alive> when wrapping dynamic components, inactive component instances are cached rather than destroyed. It does not render a DOM element on its own, nor does it appear in the parent component chain. When a component is switched within <keep-alive>, its activated and deactivated lifecycle hook functions are executed accordingly. It provides include and exclude attributes that allow components to cache conditionally.

Take a chestnut

    <keep-alive>
        <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    <router-view v-if=! "" $route.meta.keepAlive"></router-view>
Copy the code




Switch button

When a button is clicked, the two inputs switch, but the state of the two input fields is cached, and the contents of the input tag do not disappear due to component switching.

* include - a string or regular expression. Only matching components are cached. * exclude - a string or regular expression. Any matching components will not be cached.Copy the code
<keep-alive include="a">
    <component></component>
</keep-alive>
Copy the code

Caches only components whose name is A

<keep-alive exclude="a">
    <component></component>
</keep-alive>
Copy the code

Everything is cached except the component whose name is A

Lifecycle hook

Keep-alive provides two life hooks, activated and deactivated.

Keep-alive stores the component in memory and does not destroy or recreate it. Therefore, the created method of the component is not called again. Instead, the activated and deactivated hooks are used to determine whether the component is active.

In-depth implementation of the Keep-alive component

The Created hook creates a cache object that can be used as a cache container to hold vNodes.


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

created () {
    // Create a cache object
    this.cache = Object.create(null)
    // Create a key alias array (component name)
    this.keys = []
},
Copy the code

A Destroyed hook removes all component instances from the cache when the component is destroyed.

destroyed () {
    /* Iterate over all cached component instances */
    for (const key in this.cache) {
        pruneCacheEntry(this.cache, key, this.keys)
    }
},
Copy the code

:::demo

render () {
  /* Get the slot */
  const slot = this.$slots.default
  /* Get the first component */ according to the slot
  const vnode: VNode = getFirstComponentChild(slot)
  constcomponentOptions: ? VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {
    // Get component name (if component name is set, component label name is returned if not)
    constname: ? string = getComponentName(componentOptions)// Destruct object assignment constants
    const { include, exclude } = this
    if ( /* Name is not in inlcude or is returned directly to vnode */ from exlude
      // not included(include && (! name || ! matches(include, name))) ||// excluded
      (exclude && name && matches(exclude, name))
    ) {
      return vnode
    }

    const { cache, keys } = this
    constkey: ? 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]) { // Check whether the current cache exists. If yes, take the cache instance. If no, cache
      vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
      remove(keys, key)
      keys.push(key)
    } else {
      cache[key] = vnode
      keys.push(key)
      // Check whether the maximum number of cache instances is set. If the maximum number of cache instances exceeds, delete the oldest data.
      if (this.max && keys.length > parseInt(this.max)) {
        pruneCacheEntry(cache, keys[0], keys, this._vnode)
      }
    }
    // Mark vnode with cache
    vnode.data.keepAlive = true
  }
  return vnode || (slot && slot[0])}// Destroy the instance
function pruneCacheEntry (cache: VNodeCache, key: string, keys: Array
       
        , current? : VNode
       ) {
  const cached = cache[key]
  if(cached && (! current || cached.tag ! == current.tag)) { cached.componentInstance.$destroy() } cache[key] =null
  remove(keys, key)
}


/ / cache
function pruneCache (keepAliveInstance: any, filter: Function) {
  const { cache, keys, _vnode } = keepAliveInstance
  for (const key in cache) {
    constcachedNode: ? VNode = cache[key]if (cachedNode) {
      constname: ? string = getComponentName(cachedNode.componentOptions)// Component name does not match the filler condition. Destroy the instance and remove cahe
      if(name && ! filter(name)) { pruneCacheEntry(cache, key, keys, _vnode) } } } }// Filter the filter function
function matches (pattern: string | RegExp | Array<string>, name: string) :boolean {
  if (Array.isArray(pattern)) {
    return pattern.indexOf(name) > - 1
  } else if (typeof pattern === 'string') {
    return pattern.split(', ').indexOf(name) > - 1
  } else if (isRegExp(pattern)) {
    return pattern.test(name)
  }
  /* istanbul ignore next */
  return false
}


// Detect include and exclude data changes, write to cache or delete in real time
mounted () {
  this.$watch('include', val => {
    pruneCache(this, name => matches(val, name))
  })
  this.$watch('exclude', val => {
    pruneCache(this, name => ! matches(val, name)) }) },Copy the code

: : :

By checking the Vue source code, it can be seen that keep-alive passes three attributes by default: include, exclude, Max, Max maximum cacheable length

Combined with source code we can implement a configurable cache router-view

<! --exclude - a string or regular expression. Any matching components will not be cached. -->
<! --TODO matches first by checking the component's own name option, and if the name option is not available, matching its local registered name -->
<keep-alive :exclude="keepAliveConf.value">
    <router-view class="child-view" :key="$route.fullPath"></router-view>
</keep-alive>
<!-- 或者   -->
<keep-alive :include="keepAliveConf.value">
    <router-view class="child-view" :key="$route.fullPath"></router-view>
</keep-alive>
<! -- Include or exclude depends on how many pages your project needs to cache -->
Copy the code

Create a keepaliveconf.js to place the component names to be matched

  // Routing component named collection
  var arr = ['component1'.'component2'];
  export default {value: routeList.join()};
Copy the code

Configure the global method for resetting the cache

import keepAliveConf from 'keepAliveConf.js'
Vue.mixin({
  methods: {
    // Pass in the name of the component to be reset
    resetKeepAive(name) {
        const conf = keepAliveConf.value;
        let arr = keepAliveConf.value.split(', ');
        if (name && typeof name === 'string') {
            let i = arr.indexOf(name);
            if (i > - 1) {
                arr.splice(i, 1);
                keepAliveConf.value = arr.join();
                setTimeout((a)= > {
                    keepAliveConf.value = conf
                }, 500); }}},}})Copy the code

Call this.resetKeepaive (name) when appropriate, triggering keep-alive to destroy the component instance;