Vue中使用KeepAlive时仅在组件首次挂载时获取数据
实现KeepAlive缓存组件仅首次挂载请求数据的方案
你的需求是让被KeepAlive缓存的子组件,仅在首次挂载或请求地址变化时获取数据,从缓存激活时不再重复请求。可以通过以下两种方案解决:
方案一:通过状态标记控制请求时机
核心思路是用一个状态变量标记数据是否已加载,仅在首次挂载或requestUrl变化时触发请求,避免组件从缓存激活时重复执行请求逻辑。
修改子组件代码如下:
<template> <NavigationSubItem v-for="item in items" :key="item.id" /> <div v-if="error"> <button @click.prevent="refresh()">refresh</button> </div> <Loading v-if="pending" /> </template> <script setup> import { hash, ref, watch, onMounted } from 'vue'; import { useLazyFetch } from '#imports'; const props = defineProps({ requestUrl: { type: String, required: true, }, }); // 标记数据是否已加载 const isLoaded = ref(false); const items = ref([]); const pending = ref(false); const error = ref(null); let refreshFn = () => {}; // 封装数据请求逻辑 const fetchData = async () => { pending.value = true; error.value = null; try { const { data, refresh } = await useLazyFetch(() => props.requestUrl, { key: hash(props.requestUrl), server: false, // 如果不需要no-cors模式可以移除,该模式下无法读取响应内容 mode: 'no-cors', }); items.value = data.value; refreshFn = refresh; isLoaded.value = true; } catch (err) { error.value = err; } finally { pending.value = false; } }; // 首次挂载时触发请求 onMounted(() => { if (!isLoaded.value) { fetchData(); } }); // 监听requestUrl变化,重新请求数据 watch(() => props.requestUrl, () => { isLoaded.value = false; fetchData(); }, { immediate: false }); // 手动刷新方法 const refresh = () => { isLoaded.value = false; fetchData(); }; </script>
方案二:本地存储缓存请求结果(适配no-cors模式)
如果必须使用mode: 'no-cors',浏览器无法读取响应内容,此时useLazyFetch的缓存机制会失效,需要手动用本地存储缓存请求状态(注意:no-cors模式下无法获取响应数据,此方案适用于你实际能获取数据的场景,或可移除no-cors模式):
<template> <NavigationSubItem v-for="item in items" :key="item.id" /> <div v-if="error"> <button @click.prevent="refresh()">refresh</button> </div> <Loading v-if="pending" /> </template> <script setup> import { hash, ref, watch, onMounted } from 'vue'; import { useLazyFetch } from '#imports'; const props = defineProps({ requestUrl: { type: String, required: true, }, }); const isLoaded = ref(false); const items = ref([]); const pending = ref(false); const error = ref(null); // 封装带本地缓存的请求逻辑 const fetchData = async () => { const cacheKey = `nav_sub_data_${hash(props.requestUrl)}`; const cachedData = localStorage.getItem(cacheKey); // 读取本地缓存(示例设置1小时有效期) if (cachedData) { const { data, timestamp } = JSON.parse(cachedData); if (Date.now() - timestamp < 3600 * 1000) { items.value = data; isLoaded.value = true; return; } } pending.value = true; error.value = null; try { const { data } = await useLazyFetch(() => props.requestUrl, { key: hash(props.requestUrl), server: false, mode: 'no-cors', }); // 缓存数据到本地 localStorage.setItem(cacheKey, JSON.stringify({ data: data.value, timestamp: Date.now() })); items.value = data.value; isLoaded.value = true; } catch (err) { error.value = err; } finally { pending.value = false; } }; onMounted(() => { if (!isLoaded.value) { fetchData(); } }); watch(() => props.requestUrl, () => { isLoaded.value = false; fetchData(); }, { immediate: false }); const refresh = () => { isLoaded.value = false; fetchData(); }; </script>
关键说明
- KeepAlive包裹的组件,
onMounted仅执行一次,onActivated会在每次组件从缓存激活时执行,因此用isLoaded状态可以避免重复请求。 - 监听
requestUrl变化,确保当请求地址改变时能重新获取最新数据。 - 如果不需要
no-cors模式,建议移除该配置,否则无法读取响应内容,缓存逻辑也会受限。
内容的提问来源于stack exchange,提问作者Neatpixels
相关产品推荐
相关产品推荐

