一、路由滚动行为处理
默认情况下,当页面中有滚动条的时候,我们通过vue-router进行页面的切换时,你会发现滚动条的位置就像会被缓存了一样,但是通过created钩子,我们可以看到在切换路由的过程中,组件是在不断创建和销毁的,但是滚动条位置会保持和上一个页面一致。但是有的时候我们想要在切换路由的时候,希望目标路由页面滚动条在最上面,那么我们可以通过scrollBehavior进行处理,这是创建Router对象时传递的配置对象中的一个属性,值为一个函数,如:
export default new Router({
scrollBehavior(to, from, savedPosition) {
if (savedPosition) { // 浏览器前进后退按钮切换路由
return savedPosition;
} else { // 通过<router-link>切换
return {x: 0, y: 0};
}
}
});
scrollBehavior函数中的savedPosition值,如果是通过浏览器的前进后退按钮切换路由,那么savedPosition值不为null;如果是通过<router-link>进行路由切换,那么savedPosition值为null,此时就会回到目标路由页面的顶部,只有通过浏览器前进后退按钮进行切换才能保持之前的滚动条位置。
二、定制不同组件的scrollBehavior
我们可以通过路由的元信息meta
更细颗粒度控制滚动行为,即在App.vue组件mounted的时候给window添加onscroll事件,然后监听滚动条位置,并且将位置保存到当前路由的meta元信息对象中,最后在Router对象的scrollBehavior中返回目标路由的meta元信息对象中滚动条位置即可。
// App.vue
export default {
data() {
return {
timerId: -1
}
},
mounted () {
window.onscroll = this.doScroll;
},
methods: {
doScroll() {
if (this.timerId) clearTimeout(this.timerId);
this.timerId = setTimeout(() => {
// 获取页面滚动距离之后设置给当前路由的元信息
this.$route.meta.y = window.pageYOffset;
}, 300);
}
}
}
export default new Router({
scrollBehavior(to, from, savedPosition) {
return to.meta; // 返回目标路由的元数据
}
});
此时不管是通过浏览器前进后退按钮切换还是通过<router-link>切换路由都能保持之前的滚动条位置了。
三、组件内监听路由变化
当路由目标组件被缓存后,即使用了<keep-alive>包裹,那么组件的created钩子就只会被执行一次。比如有一个商品详情页面被缓存了,每当从列表页面进入详情页面的时候,我们只需要通过路由传递一个商品id,然后在created钩子内调用方法根据传递的商品id向服务器发起请求获取对应的商品详情信息,但是由于商品详情页面被缓存了,所以即使路由传递的商品id发生了变化,但是created钩子未能执行,导致商品信息并未发生变化,如:
export default {
created() {
this.getInfo(); // 当前组件被缓存,只会被执行一次
},
methods: {
getInfo(){
this.$api.get("http://localhost:3000/info", this.$route.query.id);
}
}
}
解决办法有以下几种,如:
- 在actived钩子内请求数据,如:
export default {
actived() { // 每次进入缓存组件都会执行active钩子
this.getInfo();
}
}
- 通过watch监听当前路由变化,如:
export default {
watch: {
$route(to, from) { // to相当于是新值,from相当于是旧值
if(to.path === "/detail") { // 如果进入了当前组件
this.getInfo();
}
}
}
}
- 通过组件级路由钩子beforeRouteEnter,如:
export default {
beforeRouteEnter(to, from, next) {
console.log(this); // 这里组件实例还未创建为undefined
if (to.path === "/detail") {
next((vm) => { // 回调函数内会被传入组件实例对象
vm.getInfo();
});
} else {
next();
}
}
}
需要注意的是,路由钩子的执行顺序在组件实例创建之前,所以执行到路由钩子的时候组件还未创建,这个时候this值为undefined,要想路由钩子内能访问到路由组件实例对象,那么可以在next中传递一个回调函数,等组件实例对象创建好之后就会传递给回调函数了,这个时候再调用组件内的方法即可。
四、实现从详情页返回列表页缓存数据和浏览位置,从其他页面进入到列表页则刷新数据的功能
要实现从详情页返回到列表页面能够缓存之前的数据和浏览位置,那么我们需要将List组件设置为可缓存,然后再定义一个是否使用缓存数据的变量来控制List组件数据是否需要刷新,如:
{
path: "/list",
component: List,
meta: {
keepAlive: true, // 该组件是否需要缓存,默认缓存
isUseCache: false, // 是否需要刷新数据,默认刷新
}
}
然后我们可以根据List组件的keepAlive属性来定义该组件是否需要添加上<keep-alive>进行缓存了,如:
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
接下来就是处理数据是否缓存了,我们可以在actived钩子中进行处理,默认进入List组件都是要刷新数据的,如:
actived() {
if (!this.$route.meta.isUseCache) { // 如果不使用缓存
this.list = [];
this.loadList(); // 刷新数据
}
}
现在从任何页面进入到List页面都会刷新数据,但是我们希望从详情页面进入到List页面不刷新数据,那么当从详情页面进入到List页面的时候,isUseCache值为true,所以我们可以在List页面的beforeRouteLeave()钩子中进行处理,即在离开List页面的时候进行判断,如果离开列表页面进入的是详情页面,那么就将isUseCache的值设置为true;如果离开列表页面进入的不是详情页面,那么isUseCache的值设置为false,再次进入列表页面的时候就会自动刷新数据了,如:
// 列表页面的beforeRouteLeave钩子
beforeRouteLeave(to, from, next) {
if (to.name === "Detail") {
from.meta.isUseCache = true; // 离开列表页进入详情页isUseCache为true
} else {
from.meta.isUseCache = false; // 离开列表页进入非详情页面isUseCache为false
}
next();
}
当然,我们也可以在actived钩子的最后将isUseCache设置为false,即每次进入List页面都将isUseCache设置为false,两种方法都可以,一种是每次进入设置为false,另一种是每次离开的时候设置为false。
,
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。