92 lines
2.3 KiB
JavaScript
92 lines
2.3 KiB
JavaScript
// 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 }
|
||
}
|