在做一个实战项目时,首页是一个商品列表页,引入better-scroll实现滚动效果
import BScroll from 'better-scroll';
mounted() {
this._initScroll();
},
methods: {
_initScroll() {
this.$nextTick(() => {
this.BScroll = new BScroll(this.$refs.container,{
click: true,
probeType: 3
});
this.pageOnScroll();
});
}
},
点击商品,跳转至商品详情页,返回时想要页面滚动到之前所在的位置,首先会想到vue-router的scrollBehavior方法
scrollBehavior(to, from, savePosition) {
if(savePosition) {
return savePosition
}else {
return {
x: 0,
y: 0
}
}
}
但效果却没出来,打印savePosition查看,显示为0和0
查询官方文档:
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router
能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
可以看到,官方的方法是记录整个页面的滚动,意思是,页面是由内容自动撑高的,高度不固定。但是要使用better-scroll,就必须将外围包裹层高度固定下来,这是better-scroll的滚动原理决定的
当滚动体高度覆盖了整个页面的时候,父级包裹层的高度就必须固定为屏幕的高度
.container{
width: 100%;
position: absolute;
left: 0;
top: 2.2rem;
bottom: 0;
overflow: hidden;
}
所以,整个页面的高度是固定的,不会出现页面滚动的情况,滚动的是内部的子元素,这也就难怪返回的滚动距离为0了
由于better-scroll提供了on事件的监听,可以获取滚动的距离,可以帮助实现这个效果
刚开始想着用vuex来全局存储滚动距离,既然vue-router自带的方法无法存储滚动距离,那就用better-scroll的方法获取到,然后存到全局,在监听到返回事件的时候赋值给返回值
store/index.js
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
mallScrollY: 0
},
mutations: {
scroll(state, posY) {
state.mallScrollY = posY;
}
}
});
export default store
ShoppingMall.vue
import {mapMutations} from 'vuex'
这样就把滚动距离存到了全局,在路由页面获取距离
router/index
import Store from '@/store/index'
scrollBehavior(to, from, savePosition) {
if(savePosition) {
return {
x: 0,
y: Store.state.mallScrollY
}
}else {
return {
x: 0,
y: 0
}
}
}
但是想法很美好,结果很悲催
依然没有效果,发现还是那个问题,vue-router的scrollBehavior通过监听页面滚动事件来记录滚动距离,那么返回的值也是页面应该滚动的距离,前面已经把页面高度固定了,再怎么赋值也无法滚动了。
于是只能全靠better-scroll了,还好,有scrollTo方法可以用
因为页面用了keep-alive缓存数据,所以页面跳转离开后,原页面的数据不会销毁
这样就可以把滚动数据存储在当前页面,待跳转返回来的时候拿到数据,利用better-scroll的scrollTo方法,滚动到先前的位置
pageOnScroll() {
let that = this;
this.BScroll.on('scroll', (pos) => {
console.log(pos.y)
that.scrollY = pos.y;
})
}
返回的时候,监听路由变化
watch: {
'$route'(to, from) {
let that = this;
if(to.name === 'ShoppingMall') {
if(this.BScroll) {
that.BScroll.scrollTo(0, that.scrollY, 10)
}
}
}
}
但是返回的时候,不断触发pageOnScroll函数
进入死循环,点击页面立刻就回到了顶部
后来发现,应该是由于页面缓存,之前绑定的on事件没被清除,返回时调用scrollTo方法,滚动到目标距离的过程中就会不断触发on事件,导致死循环
处理方法:
返回后先刷新一下,再滚动到目标距离
watch: {
'$route'(to, from) {
let that = this;
if(to.name === 'ShoppingMall') {
if(this.BScroll) {
this.BScroll.refresh(); // 必须先刷新一下,不然报错
that.BScroll.scrollTo(0, that.scrollY, 10)
}
}
}
}
这样就ok了