Keep-alive is used to cache components in vue

Let’s start with a simple article map:

1. What role does keep-alive play in VUE

Let’s take a look at some of the features of Keep-Alive:

When switching between components, you sometimes want to keep the state of those components to avoid performance issues caused by repeated rerendering.

Keep-alive can be used to cache dynamic components

  • Component reuse improves performance
  • Cache less-used components instead of destroying them directly

2. How to use the actual project

2.1 In normal cases, components jump

Dynamic routing is often used in projects. Here’s an example:

Create a new project using vue create hello-world. Create a new project using vue create hello-world. Create a new project using vue create hello-world. The destroyed() function destroys the component and the data is lost when we try to switch the component.

The following codeApp.vue:

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>
Copy the code

The router/index. Js:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '.. /views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/'.name: 'Home'.component: Home
  },
  {
    path: '/about'.name: 'About'.// route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () = > import(/* webpackChunkName: "about" */ '.. /views/About.vue')}]const router = new VueRouter({
  routes
})

export default router

Copy the code

view/about.vue:

<template>
  <div class="about">
    <input type="text">
  </div>
</template>
<script>
export default {
  name:"about".destroyed() {
    console.log('Component destroyed')}}</script>
Copy the code

view/home.vue:

<template>
  <div class="home">
    <input type="text">
  </div>
</template>

<script>

export default {
  name: 'home'.destroyed() {
    console.log('Component destroyed')}}</script>

Copy the code

2.2 Using keep-alive to cache components

When jumping a component, it can cache the previous component without destroying it

Just modify the app.vue component above:

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <keep-alive>
      <router-view />
    </keep-alive>
    
  </div>
</template>
Copy the code

2.3 Keep-alive Controls whether components are cached

In the actual project development, we may need a specific project to cache, so how to achieve this through keep-alive?

The keep-alive parameters are as follows:

  1. Include: [String,RegExp,Array] Only matched components can be cached
<keep-alive include="home">
   <router-view />
</keep-alive>
Copy the code
  1. Exclude: [String,RegExp,Array] Matching components are not cached
<keep-alive include="about">
   <router-view />
</keep-alive>
Copy the code
  1. Max: [String,Number] The maximum Number of component instances that can be cached. The least used instance in the cache is destroyed before a new instance is created

3. How to cache the source code in keep-alive

The source code is here:

The cacheVNdoe and lifecycle hook Render functions are analyzed below (mostly in comments).

3.1 cacheVNode ()

 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}}Copy the code

3.2 created () :

Define two properties

this.cache = Object.create(null)
this.keys = []
Copy the code

This. cache is an object used to store components that need to be cached

this.cache = {
    'key1':Components' 1 '.'key2':'component 2',,,,}Copy the code

This. Keys is an array that stores the keys of each component that needs to be cached.

3.3 destoryed ()

The hook iterates through the this.cache object and destroys any cached components that are not currently being rendered, removing them from this

destoryed() {
    for (const key in this.cache) {
        pruneCacheEntery(this.cache,key,this.keys); }}function pruneCacheEntery(cache,key,keys,current) {
    const cached = cache[key]
    if(cached &&& (! current || cached.tag ! == current.tag)) { cached.componentInstance.$destory() } cache[key] =null
    remove(keys,key)
}
Copy the code

3.4 mounted ()

In Mounted, observe the change of include exclude

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

3.5 render ()

How to cache keep-alive

  1. Gets the node of the first child component
const slot = this.$solts.default
const vnode = getFirstComponentChild(solt)
Copy the code

Only the first child is handled, so it is usually paired with a Component dynamic component or router-view.

  1. Gets the name of the component node
/* Get the name of the component node */
const name = getComponentName(componentOptions)

/* Get the component's name field first, or the component's tag */ if name does not exist
function getComponentName (opts: ? VNodeComponentOptions): ?string {
  return opts && (opts.Ctor.options.name || opts.tag)
}
Copy the code
  1. Match the component name with the general configuration rule in include Exclude
  • If name does not match the index rule or exclude rule, the component is not cached and the vNode of the component is returned
const { include, exclude } = this
if( (include && (! name || ! matches(include, name))) || (exclude && name && ! matches(exclude,name)) ) {return vnode
}
Copy the code
  • Otherwise cache
const { cache, keys } = this
// Get the key of the component
const key = vnode.key == null?
componentOptions.Ctor.cid + (componentOptions.tag ? ` : :${componentOptions.tag}` : ' ')
: vnode.key

// Select this. Cach from this. Cach to see if this value exists
/* If the cache is hit, the vNode component instance is fetched directly from the cache */
if (cache[key]) {
    vnode.componentInstance = cache[key].componentInstance
    /* Rearrange the order of the component keys, remove them from their original place and put them back in the last */  
    remove(keys, key)
    keys.push(key)
} else {
    cache[key] = vnode
    keys.push(key)
    /* If Max is configured and the cache length exceeds this. Max, the first */ is removed from the cache
    if (this.max && keys.length > parseInt(this.max)) {
        pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
}
// If not, the component is cached
Copy the code

LRU is the least recently used

When keep-alive is used, the component does not create or mounted. However, the value of activated and deactivated hook functions need to be used to perform operations in the component. The two hook functions can be invoked when the component is activated or deactivated

4. LRU caching

Adjust the order of the component key, delete it from the original place and put it back. In the comment above, there is a sentence: delete the node that is now used from the original place and put it back.

In fact, this is the core of the LRU cache mechanism. LRU (Least recently used) algorithm eliminates data according to the historical access record of data. The core idea is that “if data has been accessed recently, the probability of being accessed in the future is higher”.

Finally, finally, a force buckle algorithm: The LRU cache

var LRUCache = function(capacity) {
  this.size = capacity  // Cache size
  this.map = new Map(a)// Use map as the cache structure
};

// There are two methods on LRUCache's prototype object: get and PUT
LRUCache.prototype.get = function(key) {
// If the value obtained by get was already in the map, delete it, add it back, and return the value
  if(this.map.has(key)){  
    let value = this.map.get(key)  // Cache values first
    this.map.delete(key)           / / delete
    this.map.set(key, value)       // rejoin
    return value
  } else {                         // There is no direct return -1
    return -1}};// If it already exists, delete it and add it to the list.
// If it does not already exist, add it again.
// If the value exceeds the specified value,
// Delete the map that has not been used for the longest time, and then add the value that needs to be added.
LRUCache.prototype.put = function(key, value) {
  if(this.map.has(key)){          // Delete it
    this.map.delete(key) 
  } 
  this.map.set(key, value)        / / to join
  if(this.map.size > this.size){  // Delete the first one
  // Find the first item in the map
    this.map.delete(this.map.keys().next().value)
  }
};
Copy the code

5. To summarize

  1. Keep-alive is a built-in component of Vue that controls the cache of components. It can set three parameters to determine which components need to be cached or not, as well as the maximum cache size
  2. In the analysis of keep-Alive cache strategy, LRU cache elimination strategy is used.
  3. The core of the LRU cache strategy is that the data that is accessed (read or modified) each time is expected to be used with the greatest probability and therefore needs to be processed

The above are some summaries and generalizations about keep-alive

Comment section if you have any questions