vue3组件动态缓存与动态刷新

动态缓存

前言

在项目中,为了减少性能消耗,有时我们需要使用keep-alive把组件缓存起来,但是并不是所有组件都需要被缓存,那么如何控制那些组件需要缓存呢?主要使用到路由meta,路由前置守卫,vux,动态组件。

实现

APP.vue

<script setup>
import { ref,computed } from 'vue'
import { useRouter } from 'vue-router'
import { useAlertsStore } from './store/modules/app.js'


const store = useAlertsStore()
const includes = computed(()=>{
  return store.$state.includes
})


const router = useRouter()
const tohome = ()=>{
  router.push('/home')
}

const totest = ()=>{
  router.push('/test')
}

</script>

<template>
  <div>
    <button @click="tohome">home</button>
    <button @click="totest">test</button>
    <router-view v-slot="{Component}">
      <keep-alive :include="includes">
        <component :is="Component"></component>
      </keep-alive>
    </router-view>
  </div>
</template>

<style>
.bg {
  background-color: pink;
}
</style>

路由index.js

import { createRouter, createWebHashHistory } from 'vue-router'
import { useAlertsStore } from '../store/modules/app.js'

const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    // 导入的.vue组件名必须和组件注册的名字一致 文件名最好全小写 组件首字母最好大写
    path: '/home',
    name: 'home',
    component: () => import('../views/home.vue'),
    meta: {
      name: 'home',
      keepAlive: true
    }
  },
  {
    path: '/test',
    name: 'test',
    component: () => import('../views/testtwo.vue'),
    meta: {
      name: 'test',
      keepAlive: false
    }
  },
]


const router = createRouter({
  history: createWebHashHistory(),
  routes,
})


router.beforeEach((to, from, next) => {
  const store = useAlertsStore()
  if (to.meta.keepAlive && to.name) {
    store.setincludes({ name: to.name })
  }
  next() // 如果没传next 可以不用调用next函数 也能跳转
})



export default router

vuex

 state: () => {
    return {
      includes: [],
    }
  },
   actions: {
    // 添加需要缓存的组件
    setincludes(payload) {
      this.$state.includes = [...new Set([...this.$state.includes, payload.name])]
    },

动态刷新

前言

组件已经被我们缓存了,自然也不会重新发起网络请求请求新的数据,但是在某些情况下我们需要被缓存起来的页面重新发起ajax请求数据,这又该如何实现呢?这里主要使用到了发布订阅的思想。

实现

我们可以写一个自定义hook来封装它。
useRefreshFun

import { useAlertsStore } from '../store/modules/app.js'
import { computed, onActivated, nextTick } from 'vue'
import { useRoute } from 'vue-router'


export default function () {
  const refresh = (paramsList) => {
    const store = useAlertsStore()
    const refreshApiList = computed(() => store.$state.refreshApiList || [])
    const refreshApiFun = (pageName) => {
      //当前缓存页面没有配置刷新接口
      if (!paramsList) return
      //获取该缓存页面中的vuex的api列表
      const current = refreshApiList.value.find(
        (item) => item.pageName.toLocaleUpperCase() == pageName.toLocaleUpperCase()
      )
      // 无需要刷新的api
      if (!current) return
      current.funsName.forEach((funName) => {
        //vuex中的当前页面缓存的api集合
        const refitem = paramsList.find((refitem) => funName === refitem.name)
        //如果方法存在则刷新
        if (refitem !== undefined) refitem.funsName.forEach((fun) => fun())
      })
      nextTick(() => store.removeRefreshApi(pageName))
    }
    // 组件被激活
    onActivated(() => {
      const route = useRoute()
      const pageName = route.name
      refreshApiFun(pageName)
    })
  }

  // 添加刷新api
  const addRefreshList = (apiParams) => {
    const store = useAlertsStore()
    store.addRefreshApi(apiParams)
  }

  return {
    refresh,
    addRefreshList
  }
}

vux

import { defineStore } from 'pinia'


// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {
  state: () => {
    return {
      includes: [],
      refreshApiList: []
    }
  },
  actions: {
    // 添加需要缓存的组件
    setincludes(payload) {
      this.$state.includes = [...new Set([...this.$state.includes, payload.name])]
    },
    // 添加需要刷新的api
    addRefreshApi(payload) {
      const refreshApiList = this.$state.refreshApiList || []
      const current = refreshApiList.find((item) => item.pageName === payload.pageName)
      if (current) {
        const addFunNames = payload.funsName.filter((item) => !current.funsName.includes(item))
        current.funsName.push(...addFunNames)
      } else {
        this.$state.refreshApiList.push({
          pageName: payload.pageName,
          funsName: payload.funsName
        })
      }
    },
    // 删除需要刷新的api
    removeRefreshApi(pageName) {
      const refreshApiList = this.$state.refreshApiList || []
      const index = refreshApiList.findIndex((item) => item.pageName === pageName)
      index !== -1 && this.$state.refreshApiList.splice(index, 1)
    },
  }
})

在页面中使用
home.vue写入需要刷新的接口

<template>
  <div>
    home
    <div>
      <p>{{ num }}</p>
      <button @click="()=>num--">-</button>
      <button @click="()=>num++">+</button>
    </div>
  </div>
</template>

<script setup name="home">
import { ref } from 'vue'
import useRefreshFun from '../hooks/useRefreshFun.js'



let num = ref(0)

const queryAll = ()=>{
  num.value = 0
}

const { refresh } = useRefreshFun()

refresh([{name:'init',funsName:[queryAll]}])




</script>

<style scoped>

</style>

test.vue为触发刷新的组件

<template>
  <div>
    test
    <div>
      <p>{{ num }}</p>
      <button @click="()=>num--">-</button>
      <button @click="()=>num++">+</button>
      <button @click="refresh">触发home组件动态刷新</button>
    </div>
  </div>
</template>

<script setup name="test">
import { ref } from 'vue'
import useRefreshFun from '../hooks/useRefreshFun.js'

let num = ref(0)

const { addRefreshList } = useRefreshFun()

const refresh = ()=>{
  addRefreshList({ pageName: 'home', funsName: ['init'] })
}

</script>

<style scoped>

</style>
posted @ 2023-06-27 21:12  yunChuans  阅读(1876)  评论(0编辑  收藏  举报