vue实际项目很多时候会遇到列表页,详情页,一般在移动端通常有这样的情况:列表页——>详情页——>列表页,这时候在返回列表页时通常希望页面保存在我们进入详情页时的样子,这时候就需要用到keepAlive。
这里我们利用include来缓存我们需要缓存的页面(组件的name),同时将数据保存在store里
App.vue
<transition :name="transitionName" >
<keep-alive :include="keepAlive">
<router-view ></router-view>
</keep-alive>
</transition>
...
<script type="text/javascript">
export default {
data(){
return{
}
},
computed:{
keepAlive(){
return this.$store.state.keepAlive
}
}
}
</script>
keep-alive是vue的内置组件,include是该组件的一个prop,表示只有匹配的组件才会被缓存。
store.js
export default new Vuex.Store({
state:{
// 注意这里保存的是组件的name,不是路由router里面的name
// 注意这里保存的是组件的name,不是路由router里面的name
// 注意这里保存的是组件的name,不是路由router里面的name
keepAlive: []
},
mutations:{
setKeepAlive(state, param){
keepAlive.every(item => item != param) && keepAlive.push(param)
},
removeKeepAlive(state, param) {
let keepAlive = state.keepAlive.filter(item => item !=param)
state.keepAlive = [...keepAlive]
}
}
})
需要缓存的页面list.vue, 思路是在进入详情页之前设置缓存,在列表页返回到其他页面之前移除缓存
List.vue
export default {
// 需要缓存的页面必须要有name,否正无效
name:'list',
data(){
...
},
created (){
// 注意这里保存的是组件的name,不是路由router里面的name
// 注意这里保存的是组件的name,不是路由router里面的name
// 注意这里保存的是组件的name,不是路由router里面的name
this.$store.commit('setKeepAlive', 'list')
},
beforeRouteLeave(to, from, next){
// 这里判断是否是返回有很多方法,取决你自己
// 可以在router的meta设置index值,深度越低值越小,利用值的大小判断前进返回
// 这里直接用路由判断
if (to.name === 'detail'){ // 列表页前进到详情
this.$store.commit('setKeepAlive', 'list')
} else {// 列表页返回其他页
this.$store.commit('removeKeepAlive', 'list')
}
next()
}
}
到这里页面缓存就做好,但是测试发现并不能缓存页面中滚动元素的滚动高度,这里再稍微改动下,思路是也是写个公共方法,再需要缓存的页面再单独设置高度,这里我是在路由中统一去处理。
router.js
在需要缓存的页面添加两个参数 scrollNode
(滚动元素),scrollTop
(滚动高度)如下面列表页
// module home.js
export default [
{
path: '/', // 首页
name: 'index', // 路由页面层级
meta: {index: 1},
component: resolve => require(['@/views/Home.vue'], resolve),
},
{
path: '/list', // 列表页
name: 'list',
meta: {
index: 2, // 路由页面层级
scrollNode: '',
scrollTop: 0,
},
component: resolve => require(['@/views/List.vue'], resolve),
},
{
path: '/detail', // 详情页
name: 'detail',
meta: {
index: 3,
},
component: resolve => require(['@/views/Detail.vue'], resolve),
}
]
// index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import home from './module/home'
import store from '../store
Vue.use(VueRouter)
const routes = [
...home,
]
const router = new VueRouter({
routes
})
router.afterEach((to, from) => {
router.app.nextTick(() => {
if (to.meta.index < from.meta.index) { // 返回页面(这里index本来是为了来做vue路由切换动画的,这里正好用来判断前进/返回)
// 为了方便,这里借用了router的name来判断是否是缓存页面,所以在项目中一定要确保缓存组件的name要和对应路由name一致,
//否则需要用其他方法判断当前路由是否是缓存页面
if (store.state.keepAlive.some(item => item == to.name) && to.meta.scrollNode && to.meta.scrollTop) {
to.meta.scrollNode.scrollTop = to.meta.scrollTop
}
}
})
})
然后在list.vue页面去设置缓存高度及元素
beforeRouteLeave(to, from, next){
// 这里判断是否是返回有很多方法,取决你自己
// 可以在router的meta设置index值,深度越低值越小,利用值的大小判断前进返回
// 这里直接用路由判断
if (to.name === 'detail'){ // 列表页前进到详情
let el = document.getElementById('content') // 滚动元素
from.meta.scrollNode = el
from.meta.scrollTop = el.scrollTop
this.$store.commit('setKeepAlive', 'list')
} else {// 列表页返回其他页
from.meta.scrollNode = ''
from.meta.scrollTop = 0
this.$store.commit('removeKeepAlive', 'list')
}
next()
}
到这里就完成了,亲测有效~,如果有问题可以留言大家一起讨论讨论。