项目地址(持续迭代中):github.com/jyliyue/vit…
系列文章:
- 手拉手带你用 Vue3 + VantUI 写一个移动端脚手架 系列一 (概述)
- 手拉手带你用 Vue3 + VantUI 写一个移动端脚手架 系列二 (页面布局与兼容)
- 5 分钟带你实现一个可控制缓存销毁的 keepAlive 组件
- 手拉手带你用 Vue3 + VantUI 写一个移动端脚手架 系列三 (状态缓存管理与列表组件)
前言
移动端有个常见的业务场景,在列表页滑动进入某一个详情页,回退的时候希望列表能够停留在原先访问的位置,本篇便针对这一需求,带大家一起完善我们的列表组件,顺便结合这个列表组件 实现一个多标签列表返回定位 的案例
思路
这一功能的实现网上已有很多相关案例,但大多都使用了监听滚动事件,动态的记录滚动位置,但分析下来这一功能实现实际上并不需要实时监听,只需要在 离开页面时记录滚动位置 ,重新进入页面时赋值就好了
按照这个思路,我们需要完成三步:
- 缓存组件,即切换页面时页面组件不会被销毁
- 记录位置,页面离开时记录滚动条位置
- 重新定位,重新进入页面时定位到之前记录的位置
缓存组件
这里我们可以用之前已经实现的 <app-router-view>
组件来实现,借助 keepAlive 帮助我们实现页面离开时缓存页面的状态
关于 keepAlive 的相关讲解大家可以阅读我的另一篇文章,这里就不赘述了
传送门:5 分钟带你实现一个可控制缓存销毁的 keepAlive 组件
记录位置
这里使用我们之前封装好的列表组件 <app-list>
来进行改造
我们写一个通用方法 useScrollCache 来完成整一块滚动定位缓存这一需求的逻辑,首先我们定义一个 listRef 来获取我们列表盒子的 dom 元素,然后通过路由钩子 onBeforeRouteLeave 当页面离开前,把当前的滚动位置 listRef.value.scrollTop 记录下来,这样就完成了位置记录
<script setup> import { onBeforeRouteLeave } from 'vue-router' const { listRef } = useScrollCache() /** * @description: 滚动定位缓存 */ function useScrollCache() { const listRef = ref(null) let scrollTop = 0 onBeforeRouteLeave(() => { scrollTop = listRef.value.scrollTop }) return { listRef } } </script> <template> <div class="app-list" ref="listRef"> <van-pull-refresh v-model="refreshing" :success-text="successText" @refresh="onRefresh" > <van-list v-model:loading="loading" @load="onLoad" :finished="finished" :finished-text="finishedText" :offset="100" > <slot name="content" :list="list"> </slot> </van-list> </van-pull-refresh> </div> </template> <style lang="scss" scoped> .app-list { height: 100%; overflow-y: auto; } </style> 复制代码
重新定位
因为我们使用了keepAlive,当我们离开重新进入页面时,会触发 onActivated 生命周期,我们只要在这里把之前记录的位置重新赋值给列表就可以了
完整代码
<script setup> import { onBeforeRouteLeave } from 'vue-router' const { listRef } = useScrollCache() /** * @description: 滚动定位缓存 */ function useScrollCache() { const listRef = ref(null) let scrollTop = 0 onActivated(() => { // 激活时重新定位 listRef.value.scrollTop = scrollTop }) onBeforeRouteLeave(() => { // 离开记录位置 scrollTop = listRef.value.scrollTop }) return { listRef } } </script> 复制代码
这样,我们一个简单的列表返回记录位置的功能就实现啦,来看看效果
做一个多 Tab 列表切换案例
现在来一起使用我们封装的组件实现一个多页签列表的案例
我们使用 <van-tab>
来做多标签,使用 setTimeout 来模拟数据请求,这里需要注意一点需要把 :animated="true" 设置一下,由于不开启动画效果的话,<app-list>
实例在切换时会被销毁掉,这样处理起来逻辑就变复杂了,这里我们使用动画过度,操作体验也会更友好一点
<script setup> // This starter template is using Vue 3 <script setup> SFCs const options1 = { getData: () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ data: new Array(20), total: 30 }) }, 1000) }) } } const options2 = { getData: () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ data: new Array(20), total: 30 }) }, 1000) }) } } const activeName = ref('a') </script> <template> <app-page> <van-tabs class="app-tab-list" v-model:active="activeName" :animated="true" > <van-tab title="标签 1" name="a"> <app-list :options="options1"> <template #content="{ list }"> <van-cell v-for="(item, index) in list" :key="index" :title="index" /> </template> </app-list> </van-tab> <van-tab title="标签 2" name="b"> <app-list :options="options2"> <template #content="{ list }"> <van-cell v-for="(item, index) in list" :key="index" :title="index" /> </template> </app-list> </van-tab> <van-tab title="标签 3" name="c"> c </van-tab> </van-tabs> </app-page> </template> 复制代码
大家来一起看看效果
好了,本篇的内容比较简单也比较实用,希望能对大家完成需求有所帮助,下一篇,我们将对 Vue 自定义指令相关的知识点做讲解,并带大家一起实现一些移动端常用的指令,例如防抖截流,长按事件监听,点击复制文本等等,敬请期待!