topfans/frontend/composables/usePreload.js

92 lines
2.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// frontend/composables/usePreload.js
// Vue 3 组合式 API包装 core.get(),暴露响应式 state { data, loading, error, refresh }
import { ref, getCurrentInstance, onBeforeUnmount, watch } from 'vue'
import { get, refresh as coreRefresh, abortRequest } from '@/utils/preloadApi/core'
/**
* @param {string|Ref<string>} key - 逻辑 key
* @param {object|Ref<object>} [params] - 请求参数
* @returns {{ data: Ref, loading: Ref, error: Ref, refresh: Function }}
*
* @example
* const { data, loading, error, refresh } = usePreload('asset.detail', { id: 123 })
* // With reactive params:
* const { data, loading, error } = usePreload('asset.detail', () => ({ id: route.params.id }))
*/
export function usePreload(key, params) {
// 校验上下文
if (!getCurrentInstance()) {
console.warn('[preload] usePreload must be called in setup()')
}
const data = ref(null)
const loading = ref(true)
const error = ref(null)
let mounted = true
let fetchVersion = 0
/**
* 执行获取(不阻塞 setup
* @param {boolean} [force=false] - 跳过 TTL 缓存
*/
function doFetch(force = false) {
// 先清理上一轮 in-flight 请求
abortRequest(key, params)
const version = ++fetchVersion
loading.value = true
error.value = null
// 用 .then() 异步更新 data不阻塞 setup
const promise = force
? coreRefresh(key, params, true)
: get(key, params)
promise
.then((result) => {
if (!mounted || version !== fetchVersion) return
data.value = result
loading.value = false
})
.catch((err) => {
if (!mounted || version !== fetchVersion) return
error.value = err
loading.value = false
})
return promise
}
// 监听 params 变化key 通常是静态字符串params 动态时传入 getter
// 普通值包装为 getter 确保 Vue watch source 合法
const resolvedParams = typeof params === 'function' ? params : () => params
watch(
resolvedParams,
() => {
if (mounted) doFetch()
},
{ deep: true }
)
// 初始加载
doFetch()
// 组件卸载时清理
onBeforeUnmount(() => {
mounted = false
abortRequest(key, params)
})
/**
* 手动刷新
* @param {boolean} [force=false] - 跳过 TTL
*/
function refresh(force = false) {
return doFetch(force)
}
return { data, loading, error, refresh }
}