Desmond

Desmond

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

在Vue3的setup()中的异步函数

问题#

在使用异步的 setup() 时,必须在第一个 await 语句之前使用效果和生命周期钩子。(详情)

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('已挂载'))

    // await 语句
    await someAsyncFunction() // <-----------

    // 不起作用!
    onUnmounted(() => console.log('已卸载'))

    // 仍然可以工作,但不会自动销毁
    // 组件被销毁后会有内存泄漏!
    watch(counter, () => console.log(counter.value * 2))
  }
})

await 语句之后,以下函数将受到限制(不会自动销毁):

  • watch / watchEffect
  • computed
  • effect

以下函数将不起作用:

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

限制#

异步函数在更新跨多个组件共享的全局变量时可能会引发问题。当等待 setup() 时,多个组件的创建可能会更改全局变量,使其变得不可预测并在系统中造成混乱。如果在没有 await 语句的情况下调用 setup(),它将在第一个 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

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。