Vue3+Pinia+keep alive移动端缓存列表页

页面大致流程
在这里插入图片描述

  • 页面比较简单,只有首页能进入列表页,列表页可以进入详情页,当详情页返回列表页需要看到缓存列表页数据;
  • 如果详情页保存,返回列表页需要更新列表页数据;
  • 通过 keep-alive include包含列表页缓存页面,:include="cachedViews.views"cachedViews.views['列表页路由Name](存到store方便多处使用)
  • 列表页返回首页不需要缓存,在首页清空 cachedViews.views[]
  • 列表页需要缓存,通过路由独享的守卫处理 beforeEnter 设置 cachedViews.views['列表页路由Name]
  • 列表页面数据可能很多,还需要缓存一下滚动高度,从详情页返回需要滚动到原位置
  • 个别情况既缓存页面还需要更新列表。例如:详情页保存时, 设置 cachedViews.refresh 为 true,返回列表页判断 cachedViews.refresh 是否手动请求数据
1、页面出口处用 keep-alive 包裹

示例: App.vue

<template>
  <router-view v-slot="{ Component }">
    <keep-alive :include="cachedViews.views">
      <component :is="Component" />
    </keep-alive>
  </router-view>
</template>

<script setup lang="ts">
import { useCachedViews } from '@/store';
// 从store获取包含需要缓存的页面
const cachedViews = useCachedViews();
</script>

补充知识: <KeepAlive> 默认会缓存内部的所有组件实例,但我们可以通过 includeexclude prop
来定制该行为。这两个 prop 的值都可以是一个以英文逗号分隔的字符串、一个正则表达式,或是包含这两种类型的一个数组
示例:

<!-- 以英文逗号分隔的字符串 -->
<KeepAlive include="a,b">
  <component :is="view" />
</KeepAlive>

<!-- 正则表达式 (需使用 `v-bind`) -->
<KeepAlive :include="/a|b/">
  <component :is="view" />
</KeepAlive>

<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']">
  <component :is="view" />
</KeepAlive>
2、Store存储需要缓存哪个页面数组
export const useCachedViews = defineStore(Names.Pages, {
  state: () => {
    return {
      views: []
    }
  },
  //类似于computed 可以帮我们去修饰我们的值
  getters:{},
  //可以操作异步 和 同步提交state
  actions:{
    setViews(list: any) {
      this.views = list
    },
    getViews() {
      return this.views
    },
  }
})
3、路由独享守卫处理,是否缓存

示例:router/index.ts

import { createRouter, createWebHistory } from 'vue-router';
import { useCachedViews } from '@/store';
const cachedViews = useCachedViews()
const routerConfig = {
  history: createWebHistory('/'),
  routes: [
    {
	    path: '/home',
	    name: 'Home',
	    meta: {
	      title: '首页'
	    },
	    component: () => import('@/page/Home/index.vue'),
	    beforeEnter: (to: any, from: any, next: any) => {
	      // 首页不需要缓存列表,清空缓存数组
	      const cachedViews = useCachedViews()
	      cachedViews.setViews([])
	      next()
	    }
    },  
    {
	    path: '/list',
	    name: 'list',
	    meta: {
	      title: '列表页面'
	    },
	    component: () => import('@/page/List/index.vue'),
	    beforeEnter: (to: any, from: any, next: any) => {
	      // 进入列表页就缓存
	      const cachedViews = useCachedViews()
	      cachedViews.setViews(['list'])
	      next()
	    },
  	},
  ],
};
const router = createRouter(routerConfig);
export default router

补充知识:
路由独享的守卫,可以直接在路由配置上定义
beforeEnter 守卫只在进入路由时触发,不会在 paramsqueryhash 改变时触发。

4、列表页面根据记录高度需要重置回列表滚动高度

示例:List.vue

  • scrollHeight 记录滚动高度
  • 编辑时需要记录页面滚动高度,需要根据你的滚动元素获取,滚动高度
  • 进入列表页就以缓存,详情页回到列表页时,在onActivated事件重新设置滚动位置
// 记录滚动高度
const scrollHeight = ref()
// 查看/编辑
const handlerShowEdit = (item: any, type: string) => {
  // 保存高度
  const parentDom = document.querySelector('.log-content') as Element
  if (parentDom) {
    scrollHeight.value = parentDom.scrollTop;
  }
}
onActivated(() => {
  if (scrollHeight.value) {
    const parentDom = document.querySelector('.log-content') as Element
    parentDom.scrollTo({
      top: scrollHeight.value, //需要滚动的距离
      behavior: 'smooth',
    })
  }
  // 刷新缓存并缓存数据
  if (cachedViews.refresh) {
    // 重置刷新状态
    cachedViews.refresh = false
    loadMoreData()
  }
})
details页面保存时,返回 list 页面并刷新数据
import { useCachedViews } from '@/store';
const cachedViews = useCachedViews()
const onSubmit = () => {
	cachedViews.refresh = true
	router.back();
}

带dom及其他部分的代码,可以忽略

<template>
  <div class="search__box">
  	<!-- 导航栏 -->
    <van-nav-bar title="列表页面" fixed left-arrow @click-left="onClickBack" />
    <!-- 搜索条件忽略 -->
    <!-- 下拉刷新 -->
    <van-pull-refresh
      class="content"
      v-model="refreshing"
      loading-text="刷新中···"
      success-text="刷新成功"
      @refresh="onRefresh"
      >
      <van-list
        v-model:loading="loading"
        :finished="finished"
        :finished-text="finishedText"
        v-model:error="error"
  		error-text="请求失败,点击重新加载"
        @load="onLoad"
      >
      	<!-- 自定义列表每项展示内容 -->
        <div class="content__item" v-for="item in list" :key="item.id" @click="handlerShowEdit(item, 'view')">
          <h5>{{item?.name}}</h5>
          <p>时间: {{ item?.timer }}</p>
          <div class="content__operate">
            <span @click.stop="handlerEdit(item, 'edit')" >编辑</span>
          </div>
        </div>
      </van-list>
    </van-pull-refresh>
  </div>
</template>
<script setup lang="ts">
// 根据自己项目引入自己的接口
import { getList, delItem } from '@/service'
const refreshing = ref(true);
const loading = ref(false);
const finished = ref(false);
const finishedText = ref('没有更多了') 
const error = ref(false);
// 列表数据
const list = ref<any>([]);
const pageSize = ref(10)
const pageIndex = ref(1)
// 总条数
const totalNum = ref(0)
// 清空日志列表-重置分页数据
const clearLogList = () => {
  logList.value = []
  pageSize.value = 10
  pageIndex.value = 1
}
const showDeleteState = ref(false);
const activeId = ref('')

// 记录滚动高度
const scrollHeight = ref()

// 下拉刷新
const onRefresh = () => { // ... };
// 下拉加载更多
const onLoad = () => { // ... };
// 查看/编辑
const handlerShowEdit = (item: any, type: string) => {
  if (type == 'edit' && !item.isEdit) return false
  LogStore.setLogInfo(item)
  // 保存高度
  const parentDom = document.querySelector('.log-content') as Element
  if (parentDom) {
    scrollHeight.value = parentDom.scrollTop;
  }
  router.push({ path: `/details` })
}
// 加载更多数据
const loadMoreData = () => { // ... }
onMounted(() => {
  // 默认查询第一页数据
  getOrgData()
})
onActivated(() => {
  if (scrollHeight.value) {
    const parentDom = document.querySelector('.log-content') as Element
    parentDom.scrollTo({
      top: scrollHeight.value, //需要滚动的距离
      behavior: 'smooth',
    })
  }
  
  // 刷新缓存并缓存数据
  if (cachedViews.refresh) {
    // 重置刷新状态
    cachedViews.refresh = false
    loadMoreData()
  }
})
</script>

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值