在fetch()
成功完成之前,data
将为空,您可能正在更早地访问它.
根据您希望处理加载状态的方式,您可以将useFetch()
设置为异步,并在async setup中使用它:
export async function useFetch(url) {
const data = ref(null)
const error = ref(null)
try {
data.value = await fetch(url).then(res => res.json())
} catch(err) {
error.value = err
}
return { data, error }
}
然后在组件中:
const { data, error } = await useFetch()
或者,您还返回Promise或布尔值,以便组件可以检测数据是否正在加载.类似于:
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loadingPromise = ref()
const isLoading = ref(false)
const doFetch = () => {
isLoading.value = true
loadingPromise.value = fetch(url)
.then((res) => res.json())
.then((json) => (data.value = json))
.catch((err) => (error.value = err))
.finally(() => isLoading.value = false)
}
doFetch()
return { data, error, loadingPromise, isLoading}
}
实际上,您可以像使用异步版本一样使用它,方法是在安装过程中等待返回的promise .
以下是它的一个片段:
const { createApp, ref, defineAsyncComponent } = Vue;
function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loadingPromise = ref()
const isLoading = ref(false)
const doFetch = () => {
isLoading.value = true
loadingPromise.value = new Promise(resolve => setTimeout(resolve, 2000))
.then(() => [1,2,3])
.then((json) => (data.value = json))
.catch((err) => (error.value = err))
.finally(() => isLoading.value = false)
}
doFetch()
return { data, error, loadingPromise, isLoading}
}
const AsyncSetupComponent = {
template: `<pre>{{data}}</pre>`,
async setup(){
const {data, loadingPromise} = useFetch()
await loadingPromise.value
return {data}
}
}
const ComponentWithLoading = {
template: `
<div v-if="isLoading">Loading...</div>
<pre v-else>{{data}}</pre>
`,
setup(){
const {data, isLoading} = useFetch()
return {data, isLoading}
}
}
const App = {
components: {AsyncSetupComponent, ComponentWithLoading},
template: `
<div class="box">
Async setup component with suspense:
<suspense>
<AsyncSetupComponent/>
<template #fallback>Loading...</template>
</suspense>
</div>
<div class="box">
Component with loading:
<ComponentWithLoading/>
</div>
`
}
createApp(App).mount('#app')
.box{
border: 1px solid pink;
margin: 4px;
}
<div id="app"></div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>