preface

Hello everyone, I am Lin Sanxin, using the most easy to understand the most difficult knowledge points is my motto, the foundation is the premise of progress is my initial mind.

In retrospect, WHEN I first started writing, I was writing the Vue source code series, which is included in my nuggets column Vue source code parsing:

  • “Vue source code learning (a)” you don’t know – data responsive principle
  • Vue source code learning (2) “you don’t know – template compilation principle
  • “Vue source code learning (three)” you don’t know – first render principle
  • “Vue source code learning (4)” aims to write a study on the principle of computed and Watch that everyone can understand
  • “Vue source code learning (5)” interviewers like to ask — Vue common method source analysis
  • Do you want to know how Vuex works?
  • Do you really know how a Slot is inserted
  • 15 pictures, 20 minutes to understand the core principle of Diff algorithm, I said!!
  • Lin Sanxin drew 8 diagrams, the most understandable Vue3 response core principle analysis
  • 7 images, from zero to achieve a simple version of vue-Router, too easy to understand!

Today, let’s talk about the basic principle of keep-Alive, a common component in Vue.

scenario

Maybe you will often encounter such a scenario in your daily development: there is a List page list. vue that can be filtered, click a certain item to enter the corresponding details page, and wait until you return to list. vue from the details page, find that the List page has been refreshed! All the screening criteria are gone!!

keep-alive

What is?

  • keep-aliveIs aVue global components
  • keep-aliveIt does not render itself and does not appear in the parent component chain
  • keep-aliveWhen dynamic components are wrapped, inactive components are cached rather than destroyed

How does it work?

Keep-alive receives three parameters:

  • include: can passString, regular expression, arrayComponents whose names match are cached
  • exclude: can passString, regular expression, arrayComponents whose names match are not cached
  • max: can passdigitalTo limit the maximum number of cached components

Include and exclude are mostly passed to arrays

Dynamic components

<keep-alive :include="allowList" :exclude="noAllowList" :max="amount"> 
    <component :is="currentComponent"></component> 
</keep-alive>
Copy the code

Routing component

<keep-alive :include="allowList" :exclude="noAllowList" :max="amount">
    <router-view></router-view>
</keep-alive>
Copy the code

The source code

Component based

As mentioned earlier, keep-alive is a Vue global component that takes three arguments:

  • include: can passString, regular expression, arrayComponents whose names match are cached
  • exclude: can passString, regular expression, arrayComponents whose names match are not cached
  • max: can passdigital, limits the maximum number of cache components, exceedingmaxAccording toLRU algorithmFor replacement

By the way, let’s talk about what Keep-Alive does in its various life cycles:

  • created: Initializes oneCache, keys,, the former is used to store the virtual DOM collection of the cached component, and the latter is used to store the key collection of the cached component
  • mounted: Real-time monitoringInclude and excludeThese two changes, and perform the corresponding operations
  • destroyed: Removes all cache-related stuff

As mentioned earlier, keep-alive is not rendered to the page, so abstract is crucial!

// src/core/components/keep-alive.js

export default {
  name: 'keep-alive'.abstract: true.// Determine if this component needs to be rendered as a real DOM
  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String.Number]},created() {
    this.cache = Object.create(null) // Create an object to store the cached virtual DOM
    this.keys = [] // Create an array to store cache keys
  },
  mounted() {
    // Monitor include and exclude changes in real time
    this.$watch('include'.val= > {
      pruneCache(this.name= > matches(val, name))
    })
    this.$watch('exclude'.val= > {
      pruneCache(this.name= >! matches(val, name)) }) },destroyed() {
    for (const key in this.cache) { // Delete all caches
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },
  render() {
      / / here}}Copy the code

PruneCacheEntry function

The lifecycle destroyed deletes all caches by calling pruneCacheEntry. So let’s talk about what pruneCacheEntry does

// src/core/components/keep-alive.js

function pruneCacheEntry (
  cache: VNodeCache,
  key: string,
  keys: Array<string>, current? : VNode) {
  const cached = cache[key]
  if(cached && (! current || cached.tag ! == current.tag)) { cached.componentInstance.$destroy()// Execute the component's destory hook function
  }
  cache[key] = null  / / set to null
  remove(keys, key) // Delete the corresponding element
}
Copy the code

To sum up, we have done three things:

  • 1. Iterate through the collection, executing all cache components$destroymethods
  • 2,cacheThe correspondingkeyIs set tonull
  • 3, delete,keysThe corresponding element in

Render function

Include is a whitelist, and exclude is a blacklist

The render function basically does these things:

  • Step 1: Get itkeep-aliveThe first component of the package and itsComponent name
  • Step 2: Judge thisComponent nameIf I can beWhitelist, blacklistMatch, ifCan't be white list match | | match can be blacklisted, directly returnsVNode, do not go to the next step. If no, go to the next stepThe third step
  • Step 3: According toComponent ID and taggenerateThe cache keyAnd looks in the cache collection to see if this component has been cached. If it has been cached, simply extract the cached component and update itThe cache keyinkeysThe position in (this isLRU algorithmIf not cached, continueThe fourth step
  • Step 4: Separate inCache, keys,Kept inThis componentAnd hisThe cache keyAnd check if the quantity exceedsmax, more than the basisLRU algorithmTo delete
  • Step 5: Place the component instancekeepAliveThe property is set to true, which is very important, as we’ll see below!
// src/core/components/keep-alive.js

render() {
  const slot = this.$slots.default
  const vnode: VNode = getFirstComponentChild(slot) // Find the first child component object
  constcomponentOptions: ? VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) { // Component parameters exist
    // check pattern
    constname: ? string = getComponentName(componentOptions)/ / component name
    const { include, exclude } = this
    if ( // Condition match
      // not included(include && (! name || ! matches(include, name))) ||// excluded
      (exclude && name && matches(exclude, name))
    ) {
      return vnode
    }

    const { cache, keys } = this
    constkey: ? string = vnode.key ==null // Define the component's cache key
      // 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]) { // This component is already cached
      vnode.componentInstance = cache[key].componentInstance
      // make current key freshest
      remove(keys, key)
      keys.push(key) // Adjust the key sort
    } else {
      cache[key] = vnode // Cache component objects
      keys.push(key)
      // prune oldest entry
      if (this.max && keys.length > parseInt(this.max)) { // If the number of caches exceeds the limit, delete the first one
        pruneCacheEntry(cache, keys[0], keys, this._vnode)
      }
    }

    vnode.data.keepAlive = true // Hook functions are used to render and execute wrapped components
  }
  return vnode || (slot && slot[0])}Copy the code

Apply colours to a drawing

Vue: render Vue: render Vue: render Vue: render Vue: render Vue: render Vue: render Vue: render Vue

  • render: This function converts the component toVNode
  • patchThis function will render the data directly on the first renderingVNodeRender directly intoReal DOM“Will be picked up at the start of the second renderVNodeWill followOld VNodeContrast, patch (diff algorithm contrast occurs at this stage), and then render intoReal DOM

Keep-alive itself renders

As mentioned earlier, the keep-alive component itself will not be rendered to the page. How does that work? If the value of abstract is true, the instance will be skipped and will not appear on the parent chain

// src/core/instance/lifecycle.js

export function initLifecycle (vm: Component) {
  const options = vm.$options
  // Find the first parent component instance that is not abstract
  let parent = options.parent
  if(parent && ! options.abstract) {while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }
  vm.$parent = parent
  // ...
}
Copy the code

Package component rendering

Let’s talk more about how a component wrapped around keep-Alive uses the cache. VNode -> Real DOM takes place in patch, but this is also subdivided: VNode -> instantiate -> _update -> real DOM. The determination of the component to use the cache occurs during the instantiation phase, and this phase calls the createComponent function.

// src/core/vdom/patch.js

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
  let i = vnode.data
  if (isDef(i)) {
    const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
    if (isDef(i = i.hook) && isDef(i = i.init)) {
      i(vnode, false /* hydrating */)}if (isDef(vnode.componentInstance)) {
      initComponent(vnode, insertedVnodeQueue)
      insert(parentElm, vnode.elm, refElm) // Insert the cached DOM (vnode.elm) into the parent element
      if (isTrue(isReactivated)) {
        reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
      }
      return true}}}Copy the code
  • When loading the wrapped component for the first time, becausekeep-alivetherenderExecutes before the package component loads, so at this pointvnode.componentInstanceThe value isundefinedAnd thekeepAliveistrue, the code goes toi(vnode, false /* hydrating */)I’m not going to go down
  • When you access the wrapped component again,vnode.componentInstanceThe value of is the component instance that has been cached, then it will executeinsert(parentElm, vnode.elm, refElm)Logic, which inserts the last DOM directly into the parent element.

conclusion

I am Lin Sanxin, an enthusiastic front-end novice programmer. If you progress, like the front end, want to learn the front end, then we can make friends, touch fish ha ha, touch fish, point this –> touch fish boiling point

reference

  • The keep-alive principle is thoroughly revealed
  • Reveal the Vue technology | keep alive
  • Vue source