如何使用 Typescript 在 Vue 3.0 setup() 函数中使用 async/await

Hen*_*Jan 8 async-await typescript vue.js vuejs3 vue-composition-api

(此问题已针对 JavaScript 回答,见下文,但此问题特定于 TypeScript,其行为不同)

我正在尝试使用打字稿在 Vue3.0 中使用异步功能。

没有异步,这段代码很好用:

// file: components/HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script lang="ts">
import {defineComponent} from 'vue'

export default defineComponent({
  name: 'HelloWorld',
  props: {
    msg: String,
  },
  async setup() { // <-- this works without 'async'
    const test = 'test'

    // await doSomethingAsynchronous()

    return {
      test,
    }
  },
})
</script>
Run Code Online (Sandbox Code Playgroud)

随着async setup()组件“HelloWorld”从页面中消失,Firefox 控制台告诉我

"Uncaught (in promise) TypeError: node is null (runtime-dom.esm-bundler.js)"
Run Code Online (Sandbox Code Playgroud)

当我更改async setup()为 时setup(),代码有效,但随后我将无法在 setup 函数中使用 async/await。

所以我的问题是:如何使用 Typescript 在 setup() 函数中使用 async/await?

编辑:

这个问题的答案:为什么在 Vue3使用 async setup() 时我得到空白显示它async setup()确实适用于 JavaScript,所以我希望它也适用于 TypeScript。

gus*_*rvi 25

另一种方法是这样做:

 const users = ref([]);
 
 (async () => {
   const res = await axios.get("https://jsonplaceholder.typicode.com/users");
   users.value = res.data;
   console.log(res);
 })()

 return {
   users,
 }
Run Code Online (Sandbox Code Playgroud)

而且您不必等待它挂载,这类似于将created()与选项API一起使用。

注意:不要忘记始终有一个分号“;” 在 function 语句之前,否则 JavaScript 会认为前面的语句应该返回一个函数,例如下面的代码会导致错误“ref([]) is not a function”:

const users = ref([]) // No semicolon here

(async () => {
Run Code Online (Sandbox Code Playgroud)

防止此错误的另一种方法是始终将分号放在函数定义的同一行,以下代码也有效:

;(async () => {
Run Code Online (Sandbox Code Playgroud)

  • 当您创建立即调用的函数时,您将线程分成两个并行运行。这个答案将始终返回一个空用户,然后更新它。您应该改用悬念和异步设置。除非你想要这种更新 UI 的错误体验。 (2认同)
  • 请记住,悬念仍然被视为“实验性功能”。https://vuejs.org/guide/built-ins/suspense.html (2认同)

Bou*_*him 11

尝试使用onMounted钩子来操作异步调用:

 setup() {
    const users = ref([]);
    onMounted(async () => {
      const res = await axios.get("https://jsonplaceholder.typicode.com/users");
      users.value = res.data;
      console.log(res);
    });

    return {
      users,
    };
  },
Run Code Online (Sandbox Code Playgroud)

现场演示

  • @HendrikJan 你可以使用 onBeforeMount 而不是 onMounted (4认同)
  • 很好的答案。然而,截至 2023 年 1 月,文档表示 Suspense 是一项实验性功能,这意味着它可能不是最好的。 (4认同)
  • 为什么这是“最佳方法”? (3认同)

Wen*_* Du 9

要在生命周期挂钩之前调用 API beforeCreate(),有几种方法:

  1. 使用辅助函数(比 更优雅;(async () => {})()):
// utils.ts
export const run = (asyncFn: () => Promise<void>) => asyncFn()
Run Code Online (Sandbox Code Playgroud)
// utils.ts
export const run = (asyncFn: () => Promise<void>) => asyncFn()
Run Code Online (Sandbox Code Playgroud)
  1. 使用承诺链:
// component.vue
<script lang="ts" setup>
  import { ref } from 'vue'
  import { run } from './utils.ts'
  import { getUsers } from './api.ts'

  const users = ref([])

  run(async () => {
    // getUsers() gets called before beforeCreate()
    users.value = await getUsers()
    // ...
  })
</script>
Run Code Online (Sandbox Code Playgroud)

  • 我可以进一步说,即使异步 `onBeforeMount` 内的命令将在适当的时刻启动,但它们将在整个设置完成并安装页面后再次完成。并且它是可见的“闪烁”。因此,似乎无法在同步设置中执行任何在安装页面之前应该执行的操作。要么使用他们的“实验性”悬念,要么执行“beforeEnter”路由器防护中的代码。如果参数刚刚更改(例如用户 ID),后一个将不会触发,所以我基本上不知道该怎么做。 (2认同)