You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3异步Composable导出值无响应,如何实现状态更新通知?

问题分析与解决方案

核心问题根源

  1. 异步函数返回Promise导致解构失败:useNavigation被定义为async函数,它的返回值是一个Promise对象,而非直接的响应式对象。组件中直接解构const { main, test } = useNavigation(),实际上是从Promise上读取不存在的属性,所以初始打印结果为undefined。
  2. forEach无法处理异步等待:Object.keys(navs).forEach(async (key) => { ... })中,forEach不会等待内部的异步请求完成,虽然最终navs会被正确填充,但组件因第一步的错误根本没有拿到有效的响应式引用,导致watch无法触发。

修正后的代码实现

composables/useNavigation.js

// 全局共享状态变量
const navs = reactive({
  main: false,
  footer: false,
})

// 标记是否已加载,避免重复请求
let isLoaded = false

export const useNavigation = () => {
  const runTimeConfig = useRuntimeConfig()

  if (!isLoaded) {
    isLoaded = true
    const fetchMenus = async () => {
      const endpointMenus = `${runTimeConfig.public.API_URL}/wp-api-menus/v2/menus`
      const { data: menus } = await useFetch(endpointMenus)
      
      // 用Promise.all并行处理多个菜单请求,提升效率
      const menuRequests = Object.keys(navs).map(async (key) => {
        const menuID = menus.value?.find(m => m.slug === key)?.ID
        if (!menuID) return
        
        const endpointMenu = `${runTimeConfig.public.API_URL}/wp-api-menus/v2/menus/${menuID}`
        const { data: menu } = await useFetch(endpointMenu)
        navs[key] = menu.value
      })

      await Promise.all(menuRequests)
    }

    fetchMenus()
  }

  return {
    ...toRefs(navs),
    test: toRef(navs.main),
  }
}

/pages/[slug].vue

<script setup>
const { main, test } = useNavigation()

// 注意:在<script setup>中访问ref值需要用.value
console.log('main初始值: ', main.value)
console.log('test初始值: ', test.value)

watch(
  main,
  (newValue, oldValue) => {
    console.log('main更新后的值: ', newValue)
  },
  { deep: true, immediate: true },
)

const menu = computed(() => {
  console.log('computed中的main: ', main.value)
  return main.value
})

setTimeout(() => {
  console.log('3秒后main的值: ', main.value)
  console.log('3秒后test的值: ', test.value)
}, 3000)
</script>

关键修改说明

  • 移除async标记:将异步逻辑封装在内部函数fetchMenus中调用,让useNavigation直接返回响应式引用,避免组件拿到Promise对象。
  • 避免重复请求:添加isLoaded标记,确保菜单数据只会被请求一次,即使多个组件调用该组合式函数。
  • 并行处理异步请求:用map+Promise.all替代forEach,既保证所有异步请求完成后更新状态,又提升请求效率。
  • 正确访问ref值:在<script setup>的JavaScript代码中,需要通过.value访问ref的实际值(模板中可省略)。

内容的提问来源于stack exchange,提问作者Merc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 15:53:14