`useDetailLoader` 组合函数封装(支持自动加载 + 页面判断 + 防重复)

useDetailLoader 组合函数封装(支持自动加载 + 页面判断 + 防重复)

✅ 功能特点

  • ✅ 自动监听 query.id(或指定字段)
  • ✅ 只在指定页面名称时才触发(避免跳转到其他页面时误触发)
  • ✅ 避免同一 id 重复加载
  • ✅ 支持初始加载和浏览器返回场景
  • ✅ 支持动态更新 loading 状态

useDetailLoader.ts

// composables/useDetailLoader.ts
import { watch, ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'

interface UseDetailLoaderOptions {
  routeName: string            // 当前页面 name
  queryKey?: string            // 监听的 query 字段名,默认 'id'
  immediate?: boolean          // 是否立即加载
  fetch: (id: string) => Promise<any>  // 请求函数(返回 Promise)
  onSuccess?: (data: any) => void      // 成功回调
  onError?: (err: any) => void         // 失败回调
}

export function useDetailLoader(options: UseDetailLoaderOptions) {
  const route = useRoute()
  const idKey = options.queryKey || 'id'
  const currentId = ref<string | null>(null)
  const loading = ref(false)

  const load = async (id: string) => {
    if (!id || id === currentId.value) return
    currentId.value = id
    loading.value = true

    try {
      const data = await options.fetch(id)
      options.onSuccess?.(data)
    } catch (err) {
      options.onError?.(err)
      console.error('详情加载失败', err)
    } finally {
      loading.value = false
    }
  }

  // 初始加载
  onMounted(() => {
    const id = route.query[idKey]
    if (options.immediate && typeof id === 'string' && route.name === options.routeName) {
      load(id)
    }
  })

  // 监听 ID 变化
  watch(() => route.query[idKey], (newId) => {
    if (route.name !== options.routeName) return
    if (typeof newId === 'string') {
      load(newId)
    }
  })

  return {
    loading,
    currentId,
    reload: () => {
      const id = route.query[idKey]
      if (typeof id === 'string') {
        load(id)
      }
    }
  }
}

示例用法:详情页中加载业绩数据

const detailInfo = ref({})
const detailLoading = ref(false)

const { loading, reload } = useDetailLoader({
  routeName: 'PerformanceDetail',
  fetch: async (id) => {
    const res = await getManagerPerformanceInfo({ userId: id })
    return res.data
  },
  onSuccess: (data) => {
    detailInfo.value = data || {}
    currencySymbol.value = data.exchange || '¥'
  },
  onError: () => {
    ElMessage.error('获取详情失败')
  },
  immediate: true
})

搭配列表请求也可以:

const performanceList = ref([])
const getList = async (id: string) => {
  const res = await getPerformanceOrderList({ userId: id, ...queryParams.value })
  performanceList.value = res.rows || []
}

useDetailLoader({
  routeName: 'PerformanceDetail',
  fetch: async (id) => {
    await getList(id)
    return null // 不需要 onSuccess
  },
  immediate: true
})

支持的配置项一览

参数 类型 说明
routeName string 当前页面的路由名称
queryKey string 监听哪个 query 字段(默认 id
immediate boolean 是否在首次进入时立即加载
fetch (id) => Promise 数据获取函数
onSuccess (data) => void 请求成功时回调
onError (err) => void 请求失败时回调

总结

useDetailLoader 是一个用于详情页按需监听参数加载的通用 Hook,具备以下优势:

  • ✅ 自动监听,避免重复加载
  • ✅ 不受非当前页跳转影响
  • ✅ 可读性高,方便团队复用
  • ✅ 易于拓展支持 params、动态参数等

你可能感兴趣的:(前端,javascript,开发语言)