前言: 写此文章之前,经历了一天在项目上的折磨,由于我做的是用户端的商城,需求是嵌套N多层页面,从一级页面进入二级页面,滚动二级页面的内容到底部,保存位置,在进入三级页面,保存位置,第四级页面,保存位置…当用户不想看了,在返回上一级页面并滚动到点击时的位置。
由于保存位置需要开启 keep-alive 组件,此组件会缓存不活动的数据,那么此时 created/mounted 里的接口就不会再次调用,在返回上一级页面时,浏览器会渲染第一次进入时拿到的数据.,返回到首页,再次进入需求页面,还是保留之前的数据…
开启缓存 → 保留位置
// 1.router 中先写一个保存位置的方法
export default new Router({
scrollBehavior(to, from, savedPosition) {
// 此处理返回的是 body 的滚动高度即 document.documentElement.scrollTop
// 或document.body.scrollTop,而该项目body高度已限制死,实际是根容器#app内部在滚动
// 故上述两个滚动高度属性无效,该处理函数无效,可在需要的页面修改body及#app容器的高度
if (savedPosition) {
console.log(
'------------支持 history.pushState 的浏览器且是浏览器前进后退按钮(go(n)或back、forward)进行导航--------------')
console.log(savedPosition)
return savedPosition
} else {
console.log('------------不支持 history.pushState 的浏览器或不是浏览器前进后退按钮进行导航-------------')
if (to.path === '/shop' && ['/ActivityIndex', '/activities','/oldForNew'].includes(from.path)) {
// 从这几个页面进入首页时,如果路径有indexTop则使用路径的,
// 没有则使用页面离开时在路由元里记录的高度
let top = to.meta?.scrollTop
if (
to.query &&
to.query.indexTop !== undefined &&
to.query.indexTop !== ''
) {
top = +to.query.indexTop
}
return { x: 0, y: top || 0 }
} else {
console.log()
return { x: 0, y: 0 }
}
}
},
// 2.配置页面,开启缓存
routes: [{
path: '/themeActivities',
name: '专题活动',
component: themeActivities,
meta: {
// requireAuth: true,
keepAlive: true, // 判断页面是否缓存
},
}]
})
解决方法1: this.$route.meta.keepAlive = false / true 手动关闭/开启缓存,但是会造成二级页面去三级页面,再返回二级页面时,不会再次调用接口的情况
mounted() {
// 安卓嵌套H5
if (navigator.userAgent.indexOf('greeMall.android') > -1) {
window.nativeToJSTradeInSelfInfo = this.nativeToJSTradeInSelfInfo //获取userId,无论有值还是空值,都会执行
} else if (navigator.userAgent.indexOf('greeMall.ios') > -1) {
// iOS嵌套H5
let _that = this
storage.remove('iosUserId')
window.AppUserId = function (androidParam) {
storage.set('iosUserId', androidParam.androidUserId)
_that.getData()
}
}else {
this.getDate() // 调用接口
this.$route.meta.keepAlive = false // 手动关闭缓存
}
},
activated() {
this.$route.meta.keepAlive = false // 手动关闭缓存(组件被激活后,不能这样使用,无效的操作)
let o = storage.get('newsTop') ? storage.get('newsTop') : 0
document.getElementById('app').scrollTop = o
storage.remove('newsTop')
},
beforeRouteLeave(to, from, next) {
this.$route.meta.keepAlive = true // 手动开启缓存
let page =['/shopDetail','/couponCenter','/trial','/searchResult','/newLogin','/seckillList','/living'].includes(to.path)
if (page){
let top = document.getElementById('app').scrollTop
storage.set('newsTop', top)
}
next()
},
最终解决方法: 动态删除 keep-Alive 组件,
参考网上的链接:https://segmentfault.com/a/1190000015845117
// 3.通过导航守卫,来判断进入的页面有哪些需要记住位置
// 路由离开时( to 离开当前页面去往哪个页面 / from 来自哪个页面 / next(vm=>{}) 下一步操作 )
beforeRouteLeave(to, from, next) {
// start---此步骤非常重要!!!
// 页面离开时添加一个缓存的标识符(从首页/搜索页进入此页,动态删除缓存,不删除会造成页面刚进入时渲染之前的数据,在替换成新的数据,有一秒的延迟)
storage.session.set('couponTag', this.$vnode.tag)
// end---此步骤非常重要!!!
let page =['/shopDetail','/couponCenter','/trial','/searchResult','/newLogin','/seckillList','/living'].includes(to.path)
if (page){
let top = document.getElementById('app').scrollTop
storage.set('newsTop', top)
}
next()
},
// 4. 激活此钩子,页面一进来就滚动到点击时的位置(不刷新页面)
activated() {
let o = storage.get('newsTop') ? storage.get('newsTop') : 0
document.getElementById('app').scrollTop = o
storage.remove('newsTop')
},
// 5. 解决因为开启缓存,而造成 created/mounted 里的接口不调用的问题
beforeRouteEnter(to, from, next) {
next(vm => {
vm.getData() // 路由一进来调用数据,这里写了方法,created/mounted 里的接口方法可以注释了
})
},
// 6. 在首页的 activated 钩子中动态删除 第3步 里面缓存的标识符。
// 从首页进入需求页面(themeActivities)此步很重要,不删会造成进入时会先渲染之前数据,在渲染新数据,一秒延迟
activated() { // 在 activated里面删除与 beforeRouteLeave 里删除都一样,只不过 beforeRouteLeave里面需要添加判断条件,参考 第7点
let couponTag = storage.session.get('couponTag')?storage.session.get('couponTag'):''
let vnodeTags = [storage.session.get('searchResultTag'),couponTag]
vnodeTags.forEach(e=>{
if (e) {
let instanceList = this.$parent.$vnode.componentInstance.$children
let instance = instanceList.filter(ins => ins.$vnode.tag === e)
if (instance.length > 0) {
instance.forEach(vm => {
if (vm.$vnode && vm.$vnode.data.keepAlive) {
if (
vm.$vnode.parent &&
vm.$vnode.parent.componentInstance &&
vm.$vnode.parent.componentInstance.cache
) {
if (vm.$vnode.componentOptions) {
var key = vm.$vnode.key == null ?
vm.$vnode.componentOptions.Ctor.cid + (vm.$vnode.componentOptions.tag ? `::${vm.$vnode.componentOptions.tag}` : '')
: vm.$vnode.key
var cache = vm.$vnode.parent.componentInstance.cache
var keys = vm.$vnode.parent.componentInstance.keys
if (cache[key]) {
if (keys.length) {
var index = keys.indexOf(key)
if (index > -1) {
keys.splice(index, 1)
}
}
delete cache[key]
}
}
}
}
vm.$destroy()
})
}
storage.session.remove('searchResultTag')
storage.session.remove('couponTag') // 删除标识符
}
})
},
// 7. 搜索页去往需求页面 动态删除缓存节点
beforeRouteLeave(to, from, next) {
if(to.path =='/themeActivities'){
let vnodeTag = storage.session.get('couponTag')
if (vnodeTag) {
let instanceList = this.$parent.$vnode.componentInstance.$children
let instance = instanceList.filter(ins => ins.$vnode.tag === vnodeTag)
if (instance.length > 0) {
instance.forEach(vm => {
if (vm.$vnode && vm.$vnode.data.keepAlive) {
if (
vm.$vnode.parent &&
vm.$vnode.parent.componentInstance &&
vm.$vnode.parent.componentInstance.cache
) {
if (vm.$vnode.componentOptions) {
var key =
vm.$vnode.key == null ?
vm.$vnode.componentOptions.Ctor.cid +
(vm.$vnode.componentOptions.tag ?
`::${vm.$vnode.componentOptions.tag}` :
'') :
vm.$vnode.key
var cache = vm.$vnode.parent.componentInstance.cache
var keys = vm.$vnode.parent.componentInstance.keys
if (cache[key]) {
if (keys.length) {
var index = keys.indexOf(key)
if (index > -1) {
keys.splice(index, 1)
}
}
delete cache[key]
}
}
}
}
vm.$destroy()
})
}
storage.session.remove('couponTag')
}
}
}