Writing in the front

Because of the vue. js is very interested in, and the usual work of the technology stack vue. js, these months spent some time to study the vue. js source code, and do a summary and output.

Original address of the article: github.com/answershuto… .

In the process of learning, I added Chinese annotations for Vue github.com/answershuto… And Vuex’s notes github.com/answershuto… , I hope to be helpful to other people who want to learn source code.

There may be some misunderstanding, welcome to point out, learn together, make progress together.

keep-alive

Keep-alive is a built-in component of vue.js. It can keep inactive component instances in memory rather than destroying them directly, and it is an abstract component that is not rendered into the real DOM and does not appear in the parent component chain.

It provides include and exclude attributes that allow components to cache conditionally.

For details, please refer to the official website.

use

usage

<keep-alive>
    <component></component>
</keep-alive>Copy the code

The component component here is cached.

Take a chestnut

<keep-alive>
    <coma v-if="test"></coma>
    <comb v-else="test"></comb>
</keep-alive>
<button @click="test=handleClick">Please click on</button>Copy the code
export default {
    data () {
        return {
            test: true}},methods: {
        handleClick () {
            this.test = !this.test; }}}Copy the code

When button is clicked, coma and Comb will switch, but the state of the two components will be cached. For example, coma and Comb both have an input label, so the content in the input label will not disappear because of the switch of the components.

props

The keep-alive component provides include and exclude attributes to allow the component to cache conditionally, both of which can be represented as comma-separated strings, regular expressions, or an array.

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

The component with name A will be cached.

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

Components whose name is a will not be cached.

Life is a 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 keep-alive component is used to cache the cache of the keep-alive component.

Created and Destroyed hooks

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

created () {
    /* Cache object */
    this.cache = Object.create(null)},Copy the code

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

/* Destroyed all component instances in cache */
destroyed () {
    for (const key in this.cache) {
        pruneCacheEntry(this.cache[key])
    }
},Copy the code

render

Next comes the render function.

render () {
    /* Gets the first component in the slot slot */
    const vnode: VNode = getFirstComponentChild(this.$slots.default)

    constcomponentOptions: ? VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {
        // check pattern
        /* Get the component name, the component's name field first, otherwise the component's tag */
        constname: ? string = getComponentName(componentOptions)/* Name is not in inlcude or is returned directly from vnode (no cache is taken) */ from exlude
        if (name && (
        (this.include && ! matches(this.include, name)) ||
        (this.exclude && matches(this.exclude, name))
        )) {
            return vnode
        }
        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
        /* Get the component instance from the cache to vNode if it has already been cached, or cache it if it has not already been cached */
        if (this.cache[key]) {
            vnode.componentInstance = this.cache[key].componentInstance
        } else {
            this.cache[key] = vnode
        }
        /* keepAlive flag bit */
        vnode.data.keepAlive = true
    }
    return vnode
}Copy the code

The first child component is obtained by getFirstComponentChild, and the name of the component is used (if a component name exists, otherwise a tag is used). The name is then matched with the include and exclude attributes. If the match fails (indicating that no caching is required), vnode is returned without any operation. Vnode is an object of type VNode. For those of you who are not familiar with VNode, please refer to my article “VNode Nodes”.

/* Check if name matches */
function matches (pattern: string | RegExp, name: string) :boolean {
  if (typeof pattern === 'string') {
    /* Strings, such as a,b,c */
    return pattern.split(', ').indexOf(name) > - 1
  } else if (isRegExp(pattern)) {
    / * regular * /
    return pattern.test(name)
  }
  /* istanbul ignore next */
  return false
}Copy the code

It is a simple function to detect a match between include and exclude attributes, which support comma-separated component names such as “a,b,c” and regular expressions. Matches checks for matches to the current component in both ways, respectively.

if (this.cache[key]) {
    vnode.componentInstance = this.cache[key].componentInstance
} else {
    this.cache[key] = vnode
}Copy the code

The next thing to do is simply to look up the key in this.cache and overlay the cached VNode componentInstance onto the current VNode. Otherwise, vNodes are stored in the cache.

Finally, return the VNode whose componentInstance has been replaced in the cache.

watch

Use watch to monitor the change of pruneCache and pruneCache, and modify the cached data in the cache when the property changes.

watch: {
    /* Monitor include and exclude to modify the cache when it is modified */
    include (val: string | RegExp) {
        pruneCache(this.cache, this._vnode, name => matches(val, name))
    },
    exclude (val: string | RegExp) {
        pruneCache(this.cache, this._vnode, name => ! matches(val, name)) } },Copy the code

So let’s look at an implementation of pruneCache.

/* Fix cache */
function pruneCache (cache: VNodeCache, current: VNode, filter: Function) {
  for (const key in cache) {
    /* Fetch vnode */ from cache
    constcachedNode: ? VNode = cache[key]if (cachedNode) {
      constname: ? string = getComponentName(cachedNode.componentOptions)/* If name does not match filter criteria and is not the currently rendered Vnode, the component instance (Vue instance) corresponding to vnode is destroyed and */ is removed from cache
      if(name && ! filter(name)) {if(cachedNode ! == current) { pruneCacheEntry(cachedNode) } cache[key] =null}}}}/* Destroy the corresponding vnode component instance (Vue instance) */
function pruneCacheEntry (vnode: ? VNode) {
  if (vnode) {
    vnode.componentInstance.$destroy()
  }
}Copy the code

All entries in the cache are iterated. If the rules specified by filter are not met, pruneCacheEntry is executed. PruneCacheEntry calls the component instance’s $destroy method to destroy the component.

The last

Vue. Js internally abstracts DOM nodes into VNode nodes, and the cache of keep-Alive components is also based on VNode nodes rather than directly storing DOM structures. The pruneCache and pruneCache components are cached in the cache object, and vNodes are removed from the cache and rendered when needed.

about

Author: Ran Mo

Email: [email protected] or [email protected]

Github: github.com/answershuto

Zhihu: www.zhihu.com/people/cao-…

Please indicate the source of reprint, thank you.

Welcome to follow my public number