Answer the caching principle of keep-alive components from the perspective of source code

Answer the caching principle of keep-alive components from the perspective of source code

Today, let’s get straight to the point and talk about a question I was asked during an interview: the caching principle of the keep-alive component.

Official API introduction and usage

  • <keep-alive> When wrapping a dynamic component, inactive component instances are cached instead of being destroyed.
  • Similar to <transition>, <keep-alive> is an abstract component: it does not render a DOM element itself, nor does it appear in the component's parent component chain.
  • When a component is switched in <keep-alive>, its activated and deactivated lifecycle hook functions will be executed accordingly.

The example on the official website is that tab switching saves the user's operations. In practice, you may also encounter a situation where you jump from a list page to a detail page, and then jump back to the list page. You need to save the user's filtering operations, which requires the use of <keep-alive>, which can also avoid re-rendering and improve page performance.

Usage and props explanation

// Usage of keep-alive component with dynamic component. For other usages, please refer to the official website <keep-alive
 include="['componentNameA', 'componentNameB']"
 exclude="'componentNameC'"
 :max="10">
 <component :is="view"></component>
</keep-alive>

  • include - string or regular expression or array, components matching name will be cached
  • exclude - string or regular expression or array, components matching name will not be cached
  • max - string or number, the maximum number of cached component instances. Instances that have not been accessed for the longest time will be destroyed.

Notice:

  • <keep-alive> only renders one component in its direct line, so if you use v-for in <keep-alive>, it will not work. The same is true if there are multiple conditions that meet the conditions.
  • When include and exclude are matched, the name option of the component is checked first. If the name option is not available, its local registered name (that is, the key value of the parent component's components option) is matched. Anonymous components cannot be matched.
  • <keep-alive> will not work properly with functional components because they do not cache instances.

Source code interpretation

First post a source code picture

There are 125 rows in total, and when you put them away you see there aren't that many things. In the beginning, some methods that are needed are introduced, and then some methods that the keep-alive component itself will use are defined. Finally, a component option named keep-alive is exposed to the outside. Except for abstract, we are familiar with all these options. Among them, the render function is the most important part of the cache principle. It can also be seen that the keep-alive component is a functional component.

// The isRegExp function determines whether it is a regular expression, and remove removes a member in the array. // getFirstComponentChild gets the first valid component of the VNode array. import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'
​
type VNodeCache = { [key: string]: ?VNode }; // Cache type of cache component VNode​
// Get the component name by component name or component tag (the second point noted above)
function getComponentName (opts: ?VNodeComponentOptions): ?string {
 return opts && (opts.Ctor.options.name || opts.tag)
}
​
// Determine whether include or exclude matches the component name successfully function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
 if (Array.isArray(pattern)) {
 return pattern.indexOf(name) > -1 // include or exclude is an array} else if (typeof pattern === 'string') {
 return pattern.split(',').indexOf(name) > -1 // include or exclude is a string } else if (isRegExp(pattern)) {
 return pattern.test(name) // include or exclude is a regular expression}
 return false // None of them match (note the second and third points above)
}
​
// Destroy cache function pruneCache (keepAliveInstance: any, filter: Function) {
 const { cache, keys, _vnode } = keepAliveInstance // keep-alive component instance for (const key in cache) {
 const cachedNode: ?VNode = cache[key] // Component that has been cached if (cachedNode) {
  const name: ?string = getComponentName(cachedNode.componentOptions)
  // If name exists and does not match include or exclude, destroy the cached component if (name && !filter(name)) {
  pruneCacheEntry(cache, key, keys, _vnode)
  }
 }
 }
}
​
//Destroy the cache entry function pruneCacheEntry (
 cache: VNodeCache,
 key: string,
 keys: Array<string>,
 current?: VNode
) {
 const cached = cache[key] // Cached component // "Whether to continue caching components" when there is a change // If the component has been cached and the current component does not exist or the tag of the cached component is not equal to the tag of the current component if (cached && (!current || cached.tag !== current.tag)) {
 // This means that the component no longer needs to be cached. Destroy the component instance cached.componentInstance.$destroy()
 }
 cache[key] = null // Set this component in the cache to null
 remove(keys, key) // Remove the key of this component from the keys array}
​
// Example type const patternTypes: Array<Function> = [String, RegExp, Array]
​
// Expose some options of the keep-alive component export default {
 name: 'keep-alive', // component name abstract: true, // keep-alive is an abstract component​
 // Three props passed in when using the keep-alive component
 props: {
 include: patternTypes,
 exclude: patternTypes,
 max: [String, Number]
 },
​
 created () {
 this.cache = Object.create(null) // Store components that need to be cached this.keys = [] // Store the key of each component that needs to be cached, that is, the key value corresponding to the this.cache object},
​
 // When destroying a keep-alive component, destroy each component in the cache destroyed () {
 for (const key in this.cache) {
  pruneCacheEntry(this.cache, key, this.keys)
 }
 },
​
 // When the keep-alive component is mounted, it monitors the changes of include and exclude, and destroys the cached component when the conditions are met. mounted () {
 this.$watch('include', val => {
  pruneCache(this, name => matches(val, name))
 })
 this.$watch('exclude', val => {
  pruneCache(this, name => !matches(val, name))
 })
 },
​
 // The point is here render () {
 const slot = this.$slots.default // default slot for keep-alive components const vnode: VNode = getFirstComponentChild(slot) // Get the first valid component of the default slot // If vnode exists, take vnode's options const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
 if (componentOptions) {
  //Get the name of the first valid component
  const name: ?string = getComponentName(componentOptions)
  const { include, exclude } = this // include and exclude passed by props
  if (
  // If include exists and name does not exist or name does not match (include && (!name || !matches(include, name))) ||
  // If exclude exists and name exists or name matches (exclude && name && matches(exclude, name))
  ) {
  return vnode // Indicates that no caching is required and this component is directly returned for rendering}
  
  // If a match is made, a cache operation is required const { cache, keys } = this // The cache component of the keep-alive component and the key corresponding to the cache component
  // Get the key of the first valid component
  const key: ?string = vnode.key == null
  // The same constructor can be registered as different local components // So cid alone is not enough, let's concatenate it? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
  : vnode.key
  // If this component hits the cache if (cache[key]) {
  // This component instance is replaced with the component instance in the cache vnode.componentInstance = cache[key].componentInstance
  // Update the position of the current key in keysremove(keys, key) // Remove the current key from keyskeys.push(key) // Put it at the end of keys} else {
  // If the cache is not hit, add this component to the cache cache[key] = vnode
  keys.push(key) // Put the key of this component at the end of keys // If the number of components in the cache exceeds the max passed in, destroy the LRU component in the cache if (this.max && keys.length > parseInt(this.max)) {
   pruneCacheEntry(cache, keys[0], keys, this._vnode)
  }
  }
​
  vnode.data.keepAlive = true // Set the keepAlive property of this component to true
 }
 // If the first valid component exists but its componentOptions does not exist, return this component for rendering // Or if there is no valid first component, but the default slot of the keep-alive component exists, return the first component of the default slot for rendering return vnode || (slot && slot[0])
 }
}

Replenish:

The above order of deleting the first old cache component and updating the cache component key actually uses the LRU cache elimination strategy:
LRU stands for Least Recently Used, which means the least recently used, and is a memory management algorithm.
This algorithm is based on an assumption: data that has not been used for a long time is unlikely to be used in the future. Therefore, when the memory occupied by the data reaches a certain threshold, the least recently used data can be removed.

Summarize

A brief summary is:

When the keep-alive component is rendered, it will match the named component wrapped in the keep-alive according to the passed include and exclude. If there is no match, it will directly return the named component for rendering. If there is a match, it will perform a cache operation: if the component already exists in the cache, its instance will be replaced, and the position of the key of the component in keys will be updated; if the component does not exist in the cache, the component will be placed in the cache of the keep-alive component, and the key of the component will be placed in keys. Because include and exclude are monitored when mounted, when the values ​​of these two attributes change later, it will be determined again whether the conditions are met and the component will be destroyed.

This concludes this article on answering the caching principle of the keep-alive component from a source code perspective. For more relevant keep-alive component caching content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Example code of keep-alive built-in component cache in Vue
  • Vue keep-alive example of dynamically deleting component cache

<<:  MySQL 5.7.17 winx64 installation and configuration graphic tutorial

>>:  Windows cannot start MySQL service and reports error 1067 solution

Recommend

Detailed explanation of the pitfalls of MySQL 8.0

I updated MySQL 8.0 today. The first problem: Nav...

Detailed tutorial on installing ElasticSearch 6.4.1 on CentOS7

1. Download the ElasticSearch 6.4.1 installation ...

HTML table markup tutorial (2): table border attributes BORDER

By default, the border of the table is 0, and we ...

How to optimize MySQL index function based on Explain keyword

EXPLAIN shows how MySQL uses indexes to process s...

js implements custom drop-down box

This article example shares the specific code of ...

25 Examples of Using Circular Elements in Web Design

Today, this post lists some great examples of circ...

Docker deploys mysql to achieve remote connection sample code

1.docker search mysql查看mysql版本 2. docker pull mys...

What are the advantages of using B+ tree index in MySQL?

Before understanding this problem, let's firs...

Methods and steps for deploying multiple war packages in Tomcat

1 Background JDK1.8-u181 and Tomcat8.5.53 were in...

MySQL database aggregate query and union query operations

Table of contents 1. Insert the queried results 2...

How to open MySQL binlog log

binlog is a binary log file, which records all my...

Interactive experience trends that will become mainstream in 2015-2016

The most important interactive design article in ...

Are you still Select *?

There are many reasons why an application is as s...

React example of how to get the value of the input box

React multiple ways to get the value of the input...