首页 > 其他分享 >说说Vue的keep-alive使用及原理。

说说Vue的keep-alive使用及原理。

时间:2022-09-24 09:34:24浏览次数:42  
标签:Vue name cache alive vnode 缓存 key 组件 keep

keep-alive是vue.js的一个内置组件,它将不活动的组件保存在内存中,而非只讲将其销毁,提供了include(缓存),exclude(不缓存)两个属性,可以有选择的保存组件

语法:

<keep-alive>
    <component></component>
</keep-alive>

生命钩子

keep-alive提供了两个生命钩子,activated,deactivated,用于确定组件是否处于活动状态

原理:

created和destoryed钩子

created会创建一个cache,用来做缓存容器,保存vnode节点

created () {
    /* 缓存对象 */
    this.cache = Object.create(null)
},

destoryed则在组件被销毁时清除cache里的组件实例

/* destroyed钩子中销毁所有cache中的组件实例 */
destroyed () {
    for (const key in this.cache) {
        pruneCacheEntry(this.cache[key])
    }
},

render钩子

  • 首先获取插槽里的内容,
  • 然后调用getFirstComponentChild获取第一个子组件,如果该组件有name,就用name,没有就用tag
  • 用获取到的name和include,exclude做匹配,如果匹配失败则表示不缓存该组件,直接返回这个组件的vnode,成功则进如下一步缓存

缓存机制:

  • 用获取的name去this.cache中寻找是否有该值,如果有则表示该组件有缓存(命中缓存),此时会直接从vnode中拿组件实例,调整key的顺序将其放到最后
  • 如果没有命中缓存,则表示该组件还没有被缓存过,那么会把该组件存到cache中,如果此时超过了cache的最大缓存长度,会把第一个组件实例删除

完整render:

  render() {
    /* 获取默认插槽中的第一个组件节点 */
    const slot = this.$slots.default
    const vnode = getFirstComponentChild(slot)
    /* 获取该组件节点的componentOptions */
    const componentOptions = vnode && vnode.componentOptions
 
    if (componentOptions) {
      /* 获取该组件节点的名称,优先获取组件的name字段,如果name不存在则获取组件的tag */
      const name = getComponentName(componentOptions)
 
      const { include, exclude } = this
      /* 如果name不在inlcude中或者存在于exlude中则表示不缓存,直接返回vnode */
      if (
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }
 
      const { cache, keys } = this
      const key = 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]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        remove(keys, key)
        keys.push(key)
      } else {
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }
 
      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }

mounted钩子

调用了pruneCache方法用来观察include,exclude的变化

如果include 或exclude 发生了变化,即表示定义需要缓存的组件的规则或者不需要缓存的组件的规则发生了变化,那么就执行pruneCache函数

function pruneCache (keepAliveInstance, filter) {
  const { cache, keys, _vnode } = keepAliveInstance
  for (const key in cache) {
    const cachedNode = cache[key]
    if (cachedNode) {
      const name = getComponentName(cachedNode.componentOptions)
      if (name && !filter(name)) {
        pruneCacheEntry(cache, key, keys, _vnode)
      }
    }
  }
}

在该函数内对this.cache对象进行遍历,取出每一项的name值,用其与新的缓存规则进行匹配,如果匹配不上,则表示在新的缓存规则下该组件已经不需要被缓存,则调用pruneCacheEntry函数将其从this.cache对象剔除即可。

组件一旦被 <keep-alive> 缓存,那么再次渲染的时候就不会执行 created、mounted 等钩子函数。使用keepalive组件后,被缓存的组件生命周期会多activated和deactivated 两个钩子函数,它们的执行时机分别是 <keep-alive> 包裹的组件激活时调用和停用时调用。


完整的代码:

export default {
  name: 'keep-alive',
  abstract: true,
 
  props: {
    include: [String, RegExp, Array],
    exclude: [String, RegExp, Array],
    max: [String, Number]
  },
 
  created () {
    this.cache = Object.create(null)
    this.keys = []
  },
 
  destroyed () {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },
 
  mounted () {
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  },
 
  render() {
    /* 获取默认插槽中的第一个组件节点 */
    const slot = this.$slots.default
    const vnode = getFirstComponentChild(slot)
    /* 获取该组件节点的componentOptions */
    const componentOptions = vnode && vnode.componentOptions
 
    if (componentOptions) {
      /* 获取该组件节点的名称,优先获取组件的name字段,如果name不存在则获取组件的tag */
      const name = getComponentName(componentOptions)
 
      const { include, exclude } = this
      /* 如果name不在inlcude中或者存在于exlude中则表示不缓存,直接返回vnode */
      if (
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }
 
      const { cache, keys } = this
      const key = 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]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        remove(keys, key)
        keys.push(key)
      } else {
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }
 
      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}

 

标签:Vue,name,cache,alive,vnode,缓存,key,组件,keep
From: https://www.cnblogs.com/qianduan-Wu/p/16724928.html

相关文章

  • VUE v-bind 数据绑定
    动态的绑定一个或多个attribute,也可以是组件的prop。缩写: : 或者 . (当使用 .prop 修饰符)期望: any(带参数)|Object(不带参数)参数: attrOrProp(可选的)......
  • hadoop-zookeeper框架
    zookeeper协调机制选举leader多个flower客户端服务器特点半数以上数据一致性在有限时间范围内,执行顺序同步于发送顺序文件结构类unix树状每一个结点既是文件夹......
  • Vue2项目解决-js/css文件无法引用问题
    打包:    在vue.config.js文件中  const{defineConfig}=require('@vue/cli-service')module.exports=defineConfig({  transpileDependencies:t......
  • .NET 文件系统(七)--vue3.0文件系统搭建与配置(router配置)
    不过多介绍node安装与vue-cli安装配置,自行百度1.新建vite项目npminitvite@latest选择vue,选择ts即可生成依赖:npminstall启动项目:npmrundev2.路由(router)配置......
  • Vuex 学习笔记
    组件之间共享数据的方式小范围父向子:v-bind属性绑定简写:子向父:v-on事件绑定简写@兄弟组件之间共享数据:EventBusVuex是实现组件全局状态(数据)管理的一种机制,可......
  • Vue3与Vue2的区别(面试题)
    模板指令1、v-if、v-for优先级区别3.0依然不建议写在一个元素上但是依然可以同时绑定Vue2:当在同一个元素上使用v-if时,将优先v-forVue3:v-if优先,再v-for 2、v-model......
  • Vue组件递归渲染
    父级菜单  数据格式  子组件递归(直接使用name) ......
  • vue页面嵌套iframe页面传值、调用、首次传值获取不到、有缓存数据问题
    在父组件中创建iframe <!--iframe-->   <!-- --><el-dialog:visible.sync="dialogVisible"width="500px"@close="closeDialog"v-if="dialogVi......
  • vue组件命名错误
    Componentname“Home“shouldalwaysbemulti-worde/multi-word-component-names报错的原因:在组件命名的时候未按照ESLint的官方代码规范进行命名,根据ESLint官......
  • vue3路由简单配置
    路由目录各文件内容【router/index】import{createRouter,createWebHashHistory,createWebHistory}from"vue-router";import{scrollBehavior}from"./helpe......