问题描述
当我们在列表页面进行了搜索操作,进入详情页面后再返回,我们会发现列表页面的搜索条件被清空了。第一时间我们想到的肯定是使用,Store存储搜索栏中的数据,当页面返回的时候再重新显示搜索数据,当然这没有问题,但相比于使用keep-alive 就显得有些多余了。在移动端,在进入详情页面之后,保留列表页面的状态是更为常见的需求。
keep-alive 解决问题的原理很简单,keep-alive是vue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM,关于keep-alive更多使用方法可以查看keep-alive官方文档。
下面我们就使用 keep-alive 来更优雅的解决这个问题。
解决方法
我们的需求并不是每个页面都需要进行缓存,通常只有下面的情况我们需要缓存:
从首页–>列表页–>商详页–>返回到列表页(需要缓存)–>返回到首页(需要缓存)–>再次进入列表页(不需要缓存)。
这时候可以按需来控制页面的keep-alive,我们可以在配置路由的时候添加 isKeepAlive属性来实现。
const router = new VueRouter({ routes:[ { path:'/', name:'home', component:home, meta:{isKeepAlive:true} }, { path:'/news', name:'news', component:news, meta:{isKeepAlive:true} }, { path:'/play', component:play, meta:{isKeepAlive:false} }, ...... ] })
在渲染页面的时候,再根据isKeepAlive的配置来使用<keep-alive>对页面进行缓存。
<keep-alive v-if="route.meta.isKeepAlive"> <router-view :key="route.path" /> </keep-alive> <router-view :key="route.path" v-else />
如果你的每一个路由都是配置的不同页面组件,上面的代码已经你能完全满足需求了,不同的组件会根据 isKeepAlive 配置进行缓存。
但如果你遇到的问题也像我们的项目一样,每个页面都是通过动态生成的,也就是说所有的路由都指向同一组件 @/views/index.vue ,那就会出现所以页面都会被同时缓存或者同时不被缓存的情况,这显然不是我们配置想要达到对效果。
const router = new VueRouter({ routes:[ { path:'/', name:'home', component: () => import('@/views/index.vue'), meta:{isKeepAlive:true} }, { path:'/news', name:'news', component: () => import('@/views/index.vue'), meta:{isKeepAlive:true} }, { path:'/play', component: () => import('@/views/index.vue'), meta:{isKeepAlive:false} }, ...... ] })
为了解决这个问题可以在原组件外包装一层,使用路径作为唯一标识。通过isKeepAlive将需要缓存的页面添加到cacheRoutes实现不同页面的缓存。
import { h, ref } from 'vue' import { type RouteLocationNormalizedLoadedGeneric } from 'vue-router' // 用来存已经创建的组件 const storeComponents = new Map() // 用来存需要缓存的路由 const cacheRoutes = ref<string[]>([]) // 原组件包里面 function formatComponent(component: object, route: RouteLocationNormalizedLoadedGeneric) { let afterComponent if (component) { const path = route.path if (storeComponents.has(path)) { afterComponent = storeComponents.get(path) } else { afterComponent = { name: path, render() { return h(component) }, } if (route.meta.isKeepAlive) cacheRoutes.value.push(path) //进行缓存 storeComponents.set(path, afterComponent) } return h(afterComponent) } }
页面代码也需要改成了下面的样子,通过include指定需要缓存的组件名称。
<router-view v-slot="{ Component, route }"> <keep-alive :include="cacheRoutes"> <component :is="formatComponent(Component, route)" /> </keep-alive> </router-view>
结语
keep-alive 可以保证页面不刷新,不仅可以解决搜索框被清空的问题,还可以解决滚动了页面滚动条,进入其他页面后再返回,滚动条又回到了顶部的问题。
上面就是我对这个问题的最佳实践了,希望能对你有所帮助。