异步 Vue 生命周期

时间:2021-01-16 18:31:33

标签: vue.js

我的 Vue.js 应用程序中有以下情况:

data() {
   return {
      data: []
   }
},

async created() {
   console.log('before async call')
   try {
      // async call ...
      console.log('after async call')
      this.data = // data from api call
   } catch (err) {
      // handle err
   }
},

mounted() {
   console.log('mounted')
   init(this.data)
}

当我运行代码时,我得到:

before async call
mounted
after async call

因此,init 方法是mounted 中的类构造函数,它被调用为空数组,而不是来自API 调用的数据。我想要的是同步执行事情,并且在数据可用之前不执行挂载。我知道上面的问题是当包含异步代码时Vue如何执行生命周期,但是你如何解决这样的问题?

2 个答案:

答案 0 :(得分:2)

如果我是你,我会将所有逻辑放在 mountedcreated 钩子中。如果由于某种原因您需要等到 mount 才能让您的代码工作,请获取数据并在 mounts 中对其进行初始化:

async mounted() {
   try {
      // async call ...
      console.log('after async call')
      this.data = await // data from api call
      init(this.data)
   } catch (err) {
      // handle err
   }
}

createdmounted 之间拆分代码在性能方面几乎没有任何好处。

不过,如果由于某种原因你真的需要将它们放在不同的钩子中,你可以将 data 存储在一个 promise 中:

async created() {
   console.log('before async call')
   try {
      // async call ...
      console.log('after async call')
      this.data = fetch('whatever') // now this.data is a promise
   } catch (err) {
      // handle err
   }
},

async mounted() {
   console.log('mounted')
   init(await this.data)
}

这应该可行,但我认为不值得麻烦。

作为对可能阅读本文的任何人的澄清:您可以在 vue 生命周期钩子中使用 async/await 以便在其中进行编码以等待承诺等。但是,这不会使 Vue 实际上等待生命周期本身:因此您可以在 mounted 的异步代码之前运行 created 代码。 Vue 不等待异步生命周期这一事实并不意味着将异步/等待代码放入其中是不好的。

答案 1 :(得分:2)

Vue 中的

async 生命周期是一种误导性的语法。

每个 Vue 生命周期只是在特定时间运行您放入其中的任何代码的触发器。

但是 Vue 不会等待 promise 解决并保留其他所有内容(与组件的生命周期相关),直到它发生。实际上,您所做的只是延迟执行您放置在生命周期中的代码,直到某些承诺得到解决。

为了让您更好地了解正在发生的事情,以下语法是等效的:

async created() {
  const data = await fetchSomeData(); 
  // rest of code depending on `data`
}

等价物:

created() {
  fetchSomeData().then(data => {
    // rest of code depending on `data`
  });
}

由于 async 生命周期是一种误导性的语法,因此在大型团队开发的应用程序中通常不鼓励使用它,而是支持 .then() 语法。这是为了避免在代码实际运行时由于误解而产生的小错误。例如,如果新开发人员将一些代码放入 async 钩子中(没有仔细查看钩子中的其余代码),该代码可能会比预期运行得晚,除非放在任何 await .


要修复您要修复的任何错误,只需将在实际 data 解析为 if(内部组件)或 v-if(内部模板)。

典型用法示例:

computed: {
  someComputed() {
    if (this.data.length) {
      // returned when data has length
      return something
    }
    // returned when data has no length
    return somethingElse
  }
}

或:

<div v-if="data.length">
   <!-- markup depending on actual data... -->
</div>
<div v-else>
   loading...
</div>

注意:上面的 computed 会自动对 data 的长度变化做出反应(不需要 watch),因为 computed 属性会重新运行每当它们的内部反应引用改变值时。正如您所期望的,它们会根据它们重新计算/重新渲染任何内容。 <v-if> 也是如此。在内部,它们都使用所谓的 Vue 的“注入和反应性”