keep-alive与router-view的相爱相杀🍃

12,840 阅读4分钟

前言

由于目前做的项目都是基于tab导航的项目,目的就是要在页面跳转时实现记住上一个页面的搜索项或者表单值,减少请求,增加效率,第一反应就是keep-alive,紧接着我的噩梦就来了😫。ps:看封面图进来的记得收藏哦🍭

keep-alive概念

属性:
  • include 只有名称匹配的组件会被缓存🙄(名称指的是组件中定义的name)。
  • exclude。任何名称匹配的组件都不会被缓存🧵。
  • max。最多可以缓存多少组件实例🥙。
用法:

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

当组件在 <keep-alive> 内被切换,它的 activateddeactivated 这两个生命周期钩子函数将会被对应执行🚃,因为我们可能会需要将整个路由页面缓存下来所以就要将<router-view>进行缓存🛴。

执行顺序:
  • 页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated🦼。

router-view概念

  • <router-view>组件是一个功能组件,它为给定路径呈现匹配的组件🎨。渲染的组件<router-view>也可以包含自己的<router-view>,这将为嵌套路径渲染组件🤴。

router-view key属性(每个组件都有一个key属性,vue会复用相同组件如果需要重新渲染可以使用不同的key进行)🛒

  1. 不设置 router-viewkey 属性 由于 Vue 会复用相同组件, 即 /page/1 => /page/2 或者 /page?id=1 => /page?id=2 这类链接跳转时, 🍆将不在执行created, mounted之类的钩子, 🍱这时候你需要在路由组件中, 添加beforeRouteUpdate钩子来执行相关方法拉去数据🥯。
  1. 设置 router-viewkey 属性值为 $route.path/page/1 => /page/2, 由于这两个路由的$route.path并不一样, 🍵所以组件被强制不复用, 相关钩子从/page?id=1 => /page?id=2,🍦 由于这两个路由的$route.path一样, 所以和没设置 key 属性一样, 会复用组件🥡。
  1. 设置 router-viewkey 属性值为 $route.fullPath/page/1 => /page/2, 由于这两个路由的$route.fullPath并不一样, 所以组件被强制不复用🥙, 从/page?id=1 => /page?id=2, 由于这两个路由的$route.fullPath并不一样, 所以组件被强制不复用🥪。

写法

vue2.0的写法

# 包裹组件
# currentTabComponent 代表组件名
# 例如一个项目引用了多个组件通过相应事件修改currentTabComponent的值达到动态组件缓存的功能
<keep-alive>
   <component v-bind:is="currentTabComponent"></component>
</keep-alive>
# 包裹路由
# a,b 代表组建名称
<keep-alive :include=[a,b]>
    <router-view :key="$route.fullPath"></router-view>
</keep-alive>

vue3.0的写法

 包裹组件
# currentTabComponent 代表组件名
# 例如一个项目引用了多个组件通过相应事件修改currentTabComponent的值达到动态组件缓存的功能
<keep-alive>
   <component v-bind:is="currentTabComponent"></component>
</keep-alive>

# 包裹路由(3.0的写法有变更)key需要绑定到component中
<router-view v-slot="{ Component }">
    <keep-alive :include=[a,b]>
      <component :is="Component" :key="$route.fullPath"/>
    </keep-alive>
</router-view>

项目实战

无图言卵🤒,简单看一下我们最终实现的效果

不使用keep-alive的效果

  • 所有的页面都不会缓存,凡是涉及到路由的跳转都会执行当前组件的mounted,🎏这样就会有一些性能上的消耗,导致加载过的数据还会重新加载进行渲染在进行上方导航切换的时候不会缓存之前填写的数据🧧。

所有都使用了keep-alive的效果

  • 每个页面的第一次都会执行mounted,🧤之后再次访问该页面会进入缓存,导致我们在关闭的时候不会执行组件的销毁,加载的都是第一次加载的数据,新数据一直无法渲染🦺。

使用了keep-alive 通过includevuex控制缓存

通过这种方式当我们可以动态控制缓存的组件。

  • 当页面关闭的时候我们将不需要缓存的组件移除🍱,移除之后会执行组件的销毁生命周期🥂,在打开之前的页面会重新渲染有些时候这样就已经完美了🍟。
  • 但是事情总是事与愿违,比如上图中的编辑😴,他们是不同的路由但是指向的组件是同一个(组件name肯定是相同的)🤢,这样就会导致我们同时打开两个编辑页面的时候,每次加载都是第一次的数据😣。

最终方案:keep-alive 中的include + router-view中key

我们在上面已经提到了router-viewkey的作用🍊,所以我们只需要在router-view绑定key"$route.fullPage"🥂如果是vue3.x需要将key值绑定至component上🥂 这样我们在点击列表不同项的编辑的时候每次走的都是最新的数据🍯,同时我们使用了keep-alive中的include 上方两个编辑页进行切换的时候就会走缓存🍻,不进行页面的加载🍵。

vue3.x 设置name

<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。

  • 我们在定义变量的时候不需要在使用data
  • 方法不需要在使用methods可以直接在标签内定义变量和方法。

kepp-aliveinclude需要组件name,在vue3.x中如何定义name变量呢?

Vue3.x 会根据文件名推断组件的 name也就是说组件的文件名对应的就是name😘,如果你特别倔🤣,我就是想定义组件的名称也不是没有办法的🌯。

  1. 写两个 script 标签
  2. 如果是通过vite构建的项目可以通过插件的方式(vite-plugin-vue-setup-extend)。

1.多个script标签

<script>
    export default {
        name: 'demo'
    }
</script>

<script setup>
    // do something...
</script>
  1. vite构建插件引用
# 安装
npm i vite-plugin-vue-setup-extend -D
# 配置 vite.config.ts 
import { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
  plugins: [ VueSetupExtend()]
})
# 使用
<script lang="ts" setup name="demo">

</script>

总结

当初就是因为对keep-alive的掌握不够透彻只是知道是什么东西🍄,在真正的项目中使用才会发现有很多问题,兄弟们告辞下次见🥤,稍微有点作用就三连一下哦🤑,有错误的地方尽管在评论区展示语言😏。

参考文章

vue3.2 script setup定义的组件如何命名?

Vue2官网

Vue3官网