Desmond

Desmond

An introvert who loves web programming, graphic design and guitar
github
bilibili
twitter

在Vue3的setup()中的Async Function

問題#

在使用異步的 setup () 時,必須在第一個 await 陳述式之前使用 effects 和生命週期鉤子。(詳情)

import { ref, watch, onMounted, onUnmounted } from 'vue'

export default defineAsyncComponent({
  async setup() {
    const counter = ref(0)

    watch(counter, () => console.log(counter.value))

    // OK!
    onMounted(() => console.log('Mounted'))

    // the await statement
    await someAsyncFunction() // <-----------

    // does NOT work!
    onUnmounted(() => console.log('Unmounted'))

    // still works, but does not auto-dispose 
    // after the component is destroyed (memory leak!)
    watch(counter, () => console.log(counter.value * 2))
  }
})

在 await 陳述式之後,以下函數將受到限制(無自動處理):

  • watch / watchEffect
  • computed
  • effect

以下函數將無法運作:

  • onMounted / onUnmounted / onXXX
  • provide / inject
  • getCurrentInstance

限制#

異步函數在更新跨多個組件共享的全局變量時可能會引起問題。當等待 setup() 時,多個組件的創建可以更改全局變量,使其變得不可預測並在系統中造成混亂。如果 setup() 在沒有 await 陳述式的情況下被調用,它將在第一個 await 之前執行任務,影響執行的一致性。Vue 不能預測異步部分將在何時從外部調用,因此無法將實例綁定到上下文。

currentInstance = instance
await component.setup() // 原子性丟失
currentInstance = prev 
async function setup() {
  console.log(1)
  await someAsyncFunction()
  console.log(2)
}

console.log(3)
setup()
console.log(4)

// 輸出:
3
1
4
(awaiting)
2

解決方案#

將異步函數包裝為「反應式同步」#

const data = await fetch('https://api.github.com/').then(r => r.json())

const user = data.user

改為

const data = ref(null)

fetch('https://api.github.com/')
  .then(r => r.json())
  .then(res => data.value = res)

const user = computed(() => data?.user)

明確綁定實例#

import { effectScope, onMounted } from 'vue'

export default defineAsyncComponent({
  async setup() {
    // 在 `await` 之前獲取並保持實例
    const instance = getCurrentInstance()
    // 在 `await` 之前創建作用域,這樣它將與實例綁定
    const scope = effectScope()

    const data = await someAsyncFunction() // <-----------

    onMounted(
      () => {},
      instance // <--- 將實例傳遞給它
    )
    
    scope.run(() => {
      /* 使用 `computed`、`watch` 等等... */
    })

    // 在這裡將無法使用生命週期鉤子
  }
})

參考資料:
Anthony Fu: https://antfu.me/posts/async-with-composition-api

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。