vue-keepAlive的三个页面的进 入 场逻辑简析

自建博客文章链接:http://www.heblogs.cn/articleDetails/60fa2935b69f2b0a1af64791

场景

  • 从A页面进入B页面 ,刷新页面数据(keepAlive: ture)
  • 离开B页面进入C页面,缓存B页面数据(keepAlive: true)
  • 从C页面进入B页面,读取缓存数据 返回滚动高度(keepAlive: true)
  • 离开B页面进入A页面,不缓存B页面数据(keepAlive: false)

概念

  • keep-alive
    • keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
    • keep-alive: vue文档
  • 组件内的守卫 - beforeRouteLeave

前置代码

  • 路由元信息内添加特定字段如:keepAlive
  const router = new VueRouter({
    
    
  routes: [
    {
    
    
      path: '/foo',
      component: Foo,
      meta: {
    
     
         keepAlive: true 
      }
    }
  ]
})
  • 父组件内根据路由中的keepAlive字段动态使用keep-alive标签
<template>
  <div id="app">
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive"></router-view>
  </div>
</template>

思路

由于现在组件的keep-alive是动态根据路由元信息中的keepAlive字段进行动态使用的,所以只要动态改变对应路由元信息的keepAlive字段就可以实现动态缓存。

实现方案

方案一

  • 利用beforeRouteLeave改变from的keepAlive实现(原思路,网络解决方案之一,有bug)
beforeRouteLeave (to, from, next) {
    
    
  // 判断是否是去往页面 C 
  if (to.name === 'C') {
    
    
    // 去 C 页面,缓存
    from.meta.keepAlive = true
  } else {
    
    
    // 不是去 C 页面,不缓存
    from.meta.keepAlive = false
  }
  next()
}

bug:从A进入到B 正常刷新 进入到C 再返回B页面 缓存成功 再返回到A 进入到B 正常刷新 进入到C返回B 缓存为上次缓存的内容

方案二

  • $destroy()销毁
beforeRouteLeave (to, from, next) {
    
    
  // 导航离开该组件的对应路由时调用
  // 判断是否是去往页面 C 
  if (to.name !== 'C') {
    
    
    // 不是去 C 页面,不缓存
    this.$destroy()
  }
  next()
}

bug:销毁之后永远不会被缓存

方案三

不从开关keepalive出发 不去关闭keepalive 而是跳转带参执行activated内方法 从A页面进入

A页面:this.$router.push({
    
    path:'B'},query:{
    
    from:'A'})
B页面:
      activated () {
    
    
        const keepAliveFrom = this.$router.query.from
        if (keepAliveFrom === 'A') {
    
    
        	...
        	// 执行mounted
        }else{
    
    
        	...
        	// 执行activated
        }
      },

bug:从C物理返回B后 参数不会改变

方案四

  • 不操作beforeRouteLeave中的from对象改变keepAlive
  • 直接操作this.$router中对应路由元信息的keepAlive
// 封装操作指定name的路由的元信息
private changeKeepAlive (parentName: string, name: string, keepAlive: boolean) {
    
    
  // @ts-ignore
  this.$router.options.routes.map((item: any) => {
    
    
    if (item.name === parentName) {
    
    
        item.meta.keepAlive = keepAlive
    }
  })
}

bug:类似方案一 只是修改了操作keepalive的方式 由直接才做路由元信息去改变

方案五

改造方案三

  • 不开关keepalive 一直打开 根据页面来去方向 在activated执行方法
  • 从A C页面进入B页面 使用缓存页面记录 在B页面判断方向 调用activated内的方法

但是此方案缺点比较明显 若keepalive 全程保持打开 数据并不能初始化 需要一个一个去初始化

A页面:this.$router.push({
    
    path:'B'})
	  localStroge.setItem('keepAliveFrom','A')
B页面:
	  beforeRouteLeave (to, from, next) {
    
    
        localStorage.removeItem('keepAliveFrom') // 当离开页面 清除keepAliveFrom 下次进入走activated
    	next()
      },
      
      activated () {
    
    
        const keepAliveFrom = localStroge.getItem('keepAliveFrom')
        if (keepAliveFrom === 'my') {
    
    
        	...
        	// 执行mounted
        }else{
    
    
        	...
        	// 执行activated
        }
      },

总结:以上方案总会出点大大小小的bug 具体实现效果与预知效果差距太远,差距原因主要在于在APP.vue文件内有两个router-view 由keepalive控制开关 所以会导致keepalive不能开了就更新

方案六

在路由表路由信息给需要缓存的路由加上name name名字要与组件内名字一致

 const router = new VueRouter({
    
    
  routes: [
    {
    
     path: '/A', name:'A', component: A },
    {
    
     path: '/B', name:'B', component: B },
    {
    
     path: '/C', name:'C', component: C }
  ]
})

父组件内根据路由中的router-view标签用keep-alive包裹起来 加上属性include

<template>
  <div id="app">
    <keep-alive :include="keepAlive">
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

<script>
export default {
    
    
  name: 'App',
  data () {
    
    
    return {
    
    }
  },
  computed: {
    
    
    keepAlive () {
    
    
      return this.$store.state.keepAlive
    }
  }
}
</script>

おすすめ

転載: blog.csdn.net/weixin_45815859/article/details/109647163