vue记录滚动位置

1.目的

列表页进入详情页时缓存并记录滚动位置,返回列表页面不重新请求数据并滚动到离开时的位置

2.实现

2.1.单页面

不管是浏览器前后退 还是router-link前后退都生效

    <router-view v-slot="{ Component }">
      <keep-alive :include="['NewBook']">
        <component :is="Component" />
      </keep-alive>
    </router-view>

// 滚动位置
    let scrollTop = 0;
    onActivated(() => {
      // 调用时机为首次挂载
      // 以及每次从缓存中被重新插入时
      document.documentElement.scrollTop = scrollTop; // 滚到之前的位置
    })

    onBeforeRouteLeave((to, from) => {
      scrollTop = document.documentElement.scrollTop; // 记住滚动位置
    })

2.2 多个或所有页面(浏览器前后退生效)

<router-view v-slot="{ Component }">
      <keep-alive :include="['NewBook','SearchResults','Recommend','NewArrival','CatalogList']">
        <component :is="Component" />
      </keep-alive>
</router-view>
const router = createRouter({
    history: createWebHashHistory(),
    routes,
    scrollBehavior(to,from,savePosition){
        console.log(11,savePosition)
        return new Promise((resolve, reject) => {
            if(savePosition){// 浏览器前进后退按钮切换路由
                resolve(savePosition)
            }
            resolve({ left: 0, top: 0 }) // // 通过<router-link>切换
        })
    }
});

也可以在路由上设置meta:{keepAlive:true}来判断是否保存滚动位置。

2.3 多个页面(任何回退都生效)

// 实际上这里的keepalive 是为了保存页面的状态比如说筛选 检索结果等  
<router-view v-slot="{ Component }">
      <keep-alive :include="['NewBook','SearchResults','Recommend','NewArrival','CatalogList']">
        <component :is="Component" />
      </keep-alive>
</router-view>
路由设置keepAlive:true
      {
            path: 'newBook',//新书速递-BookBlock组件
            name: 'NewBook',
            component: () => import('@/views/NewBook.vue'),
            meta: {keepAlive:true}
        },
// 设置了以下两个方法  滚动位置无论是否使用keepalive都生效  前提是列表数据不会变化
router.beforeEach((to, from, next) => {
    // 记录页面滚动位置
    if (from.meta.keepAlive) {
        store.commit('setScrollTop',window.pageYOffset);  //可以使用 document.documentElement.scrollTopt 对页面进行相同的操作(Safari 除外,而应该使用 document.body.scrollTop 代替)
    }
    next()
})
// 页面滚动到指定位置
router.afterEach((to, from) => {
     if (to.meta.keepAlive) {
        setTimeout(() => {
            window.scrollTo(0,store.state.scrollTop);// 页面滚动到指定位置
        }, 50)
    } else {
        window.scrollTo(0,0);
    }
})

2.4 单个元素的滚动

同样 需要设置keepAlive
<router-view v-slot="{ Component }">
      <keep-alive :include="['CatalogList']">
        <component :is="Component" />
      </keep-alive>
</router-view>

<van-tree-select
      v-model:main-active-index="activeIndex"
      height="calc(100vh - 116px)"
      :items="itemList"
      @click-nav="clickNav"
      ref="treeList"
  >
    <template #content>
      <BookBlock :list="catalogList"/>
    </template>
  </van-tree-select>
  // 滚动位置
  const treeList = ref();
    let scrollTopTree = 0;
    let scrollTopBook = 0;
    onActivated(() => {
      // 调用时机为首次挂载
      // 以及每次从缓存中被重新插入时
      treeList.value.$el.children[0].scrollTop = scrollTopTree; // 滚到之前的位置
      treeList.value.$el.children[1].scrollTop = scrollTopBook; // 滚到之前的位置
    })

    onBeforeRouteLeave((to, from) => {
      scrollTopTree = treeList.value.$el.children[0].scrollTop; // 记住滚动位置
      scrollTopBook = treeList.value.$el.children[1].scrollTop; // 记住滚动位置
    })

2.3 终极大法

上面2.3的方法会导致从列表页进入或者后退都会保持列表页的状态,数据不会刷新, 实际上有时候从首页进入列表页的时候需要刷新数据的
因此需要实现从列表页前进到详情页面记录状态和滚动位置,从列表页后退不需要记录
首先设置路由

    {
            path: 'newBook',
            name: 'NewBook',
            component: () => import('@/views/NewBook.vue'),
            meta: {keepAlive:true, deepth:2} // deepth是用来判断前进后退的
        },

设置动态添加keepAlive 这里是记录页面数据状态等

 <router-view v-slot="{ Component }">
      <keep-alive :include="includeNames" :max="1">
        <component :is="Component" />
      </keep-alive>
  </router-view>


router.beforeEach((to, from) => {
			// 下面操作主要是为了解决数据刷新问题
			// 如果要to(进入)的页面是需要keepAlive缓存的,把name push进include数组中
			if (to.meta.keepAlive) {
				!includeNames.value.includes(to.name) && includeNames.value.push(to.name);
			}
			// 如果 要 form(离开) 的页面是 keepAlive缓存的,
			// 再根据 deepth 来判断是前进还是后退
			// 如果是后退就去除
			if (from.meta.keepAlive && to.meta.deepth < from.meta.deepth) {
				const index = includeNames.value.indexOf(from.name);
				index !== -1 && includeNames.value.splice(index, 1);
			}
		})

设置路由钩子 这里是记录滚动位置

// 滚动位置记录
router.beforeEach((to, from, next) => {
    // 记录页面滚动位置
    if (from.meta.keepAlive) {
        store.commit('setScrollTop', window.pageYOffset);
    }
    next()
})
/**
 * 后置钩子
 */
router.afterEach((to,from) => {
    if (to.meta.keepAlive && to.meta.deepth < from.meta.deepth ) {
        setTimeout(() => {
            window.scrollTo(0, store.state.scrollTop);// 页面滚动到指定位置
        }, 50)
    } else {
        window.scrollTo(0, 0);
    }
});
posted @ 2023-06-14 09:40  DurianTRY  阅读(437)  评论(0编辑  收藏  举报