這篇文章帶大家深入了解一下Vue3 Suspense,聊聊Suspense是什麼、能做什麼,以及如何使用它,希望對大家有幫助!
Suspense 不是你想的那樣。是的,它幫助我們處理非同步元件,但它的作用遠不止於此。 (學習影片分享:vue影片教學)
Suspense 允許我們協調整個應用程式的載入狀態,包括所有深度嵌套的元件。而不是像一個爆米花用戶界面一樣,到處都是loading,組件突然奔的一下到位
#有了Suspense, 我們可以有一個單一的、有組織的系統,一次性加載所有東西。
而且,Suspense 也為我們提供了細粒度的控制,所以如果需要的話,我們可以在兩者之間實現一些東西。
在這篇文章中,我們將學到很多關於 Suspense 的知識--它是什麼,能幹什麼,以及如何使用它。
首先,我們將仔細看看這些爆米花介面。然後,在看看如何使用Suspense來解決這些問題。之後,嘗試透過在整個應用程式中嵌套Suspense來獲得更精細的控制。最後,簡單看看如何使用佔位符來豐富我們的使用者介面。
範例地址:https://codesandbox.io/s/uncoordinated-loading-before-suspense-srh8ll?file= /src/App.vue
如果沒有Suspense,每個元件都必須單獨處理其載入狀態。
這可能會導致一些相當糟糕的使用者體驗,多個載入按鈕和內容突然出現在螢幕上,就像你在製作爆米花一樣。
雖然,我們可以建立抽像元件來處理這些載入狀態,但這比使用Suspense要困難得多。有一個單一的點來管理載入狀態,比每個元件做自己的事情要容易維護得多。
在例子中,我們使用BeforeSuspense
元件來模擬一個內部處理載入狀態的元件。把它命名為BeforeSuspense
,因為一旦我們實作了Suspense,我們就會把它重構為WithSuspense
元件。
BeforeSuspense.vue
<template> <div class="async-component" :class="!loading && 'loaded'"> <Spinner v-if="loading" /> <slot /> </div> </template> <script setup> import { ref } from 'vue' import Spinner from './Spinner.vue' const loading = ref(true) const { time } = defineProps({ time: { type: Number, default: 2000 } }) setTimeout(() => (loading.value = false), time) </script>
一開始設定 loading 為 true
,所以顯示 Spinner
元件。然後,當setTimeout
完成後,將 loading 設為 false
,隱藏 Spinner
並使元件的背景為綠色。
在這個元件中,也包含一個slot
,這樣其它元件就可以套在BeforeSuspense
元件中了:
<template> <button @click="reload">Reload page</button> <BeforeSuspense time="3000"> <BeforeSuspense time="2000" /> <BeforeSuspense time="1000"> <BeforeSuspense time="500" /> <BeforeSuspense time="4000" /> </BeforeSuspense> </BeforeSuspense> </template>
沒有什麼太花俏的東西。只是一些嵌套的組件,有不同的時間值傳遞給它們。
下面,我們來看看怎麼透過使用 Suspense
元件來改進這個爆米花使用者介面。
範例網址:https://codesandbox.io/s/coordinated-loading-with-suspense-b6dcbi?file=/src/App.vue
現在,我們使用Suspense
來處理這些亂七八糟的東西,並將其變成一個更好的使用者體驗。
不過,首先我們需要快速了解Suspense到底是什麼
以下是Suspense 部分的基本架構
<Suspense> <!-- Async component here --> <template #fallback> <!-- Sync loading state component here --> </template> </Suspense>
為了使用Suspense,把非同步元件放入default
槽,把回退載入狀態放入fallback
槽。
非同步元件是以下兩種情況之一:
async setup
函數的元件,該元件傳回一個Promise,或在script setup
中使用頂級await
defineAsyncComponent
非同步載入的元件fallback# 槽中的內容。然後,當Promise 被解決時,它會在
default 槽中顯示該非同步元件。
注意: 这里没有错误处理路基。起初我以为有,但这是对悬念的一个常见误解。如果想知道是什么导致了错误。可以使用onErrorCaptured
钩子来捕捉错误,但这是一个独立于Suspense的功能。
现在我们对Suspense有了一些了解,让我们回到我们的演示应用程序。
为了让Suspense
管理我们的加载状态,首先需要将BeforeSuspense
组件转换成一个异步组件
我们将它命名为 WithSuspense,内容如下:
<template> <div class="async-component loaded"> <!-- 这里不需要一个 Spiner 了,因为加载是在根部处理的 --> <slot /> </div> </template> <script setup> const { time } = defineProps({ time: { type: Number, required: true } }) // 加入一个延迟,以模拟加载数据 await new Promise(resolve => { setTimeout(() => { resolve() }, time) }) </script>
我们已经完全删除了加载状态的Spinner,因为这个组件不再有加载状态了。
因为这是一个异步组件,setup
函数直到它完成加载才会返回。该组件只有在 setup 函数完成后才会被加载。因此,与BeforeSuspense
组件不同,WithSuspense
组件内容在加载完毕之前不会被渲染。
这对任何异步组件来说都是如此,不管它是如何被使用的。在setup函数返回(如果是同步的)或解析(如果是异步的)之前,它不会渲染任何东西。
有了WithSuspense组件,我们仍然需要重构我们的App
组件,以便在Suspens
e组件中使用这个组件。
<template> <button @click="reload">Reload page</button> <Suspense> <WithSuspense :time="2000"> <WithSuspense :time="1500" /> <WithSuspense :time="1200"> <WithSuspense :time="1000" /> <WithSuspense :time="5000" /> </WithSuspense> </WithSuspense> <template #fallback> <Spinner /> </template> </Suspense> </template>
结构和之前一样,但这次是在 Suspense 组件的默认槽中。我们还加入了 fallback 槽,在加载时渲染我们的Spinner组件。
在演示中,你会看到它显示加载按钮,直到所有的组件都加载完毕。只有在那时,它才会显示现在完全加载的组件树。
如果你仔细注意,你会注意到这些组件并不像你想象的那样是并联加载的。
总的加载时间不是基于最慢的组件(5秒)。相反,这个时间要长得多。这是因为Vue只有在父异步组件完全解析后才会开始加载子组件。
你可以通过把日志放到WithSuspense组
件中来测试这一点。一个在安装开始跟踪安装,一个在我们调用解决之前。
最初使用BeforeSuspense
组件的例子中,整个组件树被挂载,无需等待,所有的 "异步 "操作都是并行启动的。这意味着Suspense
有可能通过引入这种异步瀑布而影响性能。所以请记住这一点。
事例地址:https://codesandbox.io/s/nesting-suspense-wt0q7k?file=/src/App.vue
这里有一个深度嵌套的组件,它需要整整5秒来加载,阻塞了整个UI,尽管大多数组件加载完成的时间要早得多。
但对我们来说有一个解决方案
通过进一步嵌套第二个Suspense组件,我们可以在等待这个组件完成加载时显示应用程序的其他部分。
<template> <button @click="reload">Reload page</button> <Suspense> <WithSuspense :time="2000"> <WithSuspense :time="1500" /> <WithSuspense :time="1200"> <WithSuspense :time="1000" /> <!-- Nest a second Suspense component --> <Suspense> <WithSuspense :time="5000" /> <template #fallback> <Spinner /> </template> </Suspense> </WithSuspense> </WithSuspense> <template #fallback> <Spinner /> </template> </Suspense> </template>
将其包裹在第二个Suspense
组件中,使其与应用程序的其他部分隔离。Suspense组件本身是一个同步组件,所以当它的父级组件被加载时,它就会被加载。
然后它将显示它自己的 fallback 内容,直到5秒结束。
通过这样做,我们可以隔离应用程序中加载较慢的部分,减少我们的首次交互时间。在某些情况下,这可能是必要的,特别是当你需要避免异步瀑布时。
从功能的角度来看,这也是有意义的。你的应用程序的每个功能或 "部分"都可以被包裹在它自己的Suspense
组件中,所以每个功能的加载都是一个单一的逻辑单元。
当然,如果你用 "Suspense" 包装每一个组成部分,我们就会回到我们开始的地方。我们可以选择以任何最合理的方式来批处理我们的加载状态。
事例地址: https://codesandbox.io/s/placeholders-and-suspense-k5uzw0?
与其使用单一的 spinner,占位符组件往往可以提供更好的体验。
这种方式向用户展示将要展示的内容,并让他们在界面渲染前有一种期待的感觉。这是 spinner 无法做到的。
可以说--它们很时髦,看起来很酷。因此,我们重构代码,使用占位符方式:
<template> <button @click="reload">Reload page</button> <Suspense> <WithSuspense :time="2000"> <WithSuspense :time="1500" /> <WithSuspense :time="1200"> <WithSuspense :time="1000" /> <Suspense> <WithSuspense :time="5000" /> <template #fallback> <Placeholder /> </template> </Suspense> </WithSuspense> </WithSuspense> <template #fallback> <!-- 这里,复制实际数据的形状 --> <Placeholder> <Placeholder /> <Placeholder> <Placeholder /> <Placeholder /> </Placeholder> </Placeholder> </template> </Suspense> </template>
我们安排了这些Placeholder组件,并对它们进行了风格化处理,使它们看起来与WithSuspense
组件完全一样。这提供了一个在加载和装载状态之间的无缝过渡。
在演示中,Placeholder
组件在背景上给我们提供了一个CSS动画,以创造一个脉动的效果:
.fast-gradient { background: linear-gradient( to right, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.4) ); background-size: 200% 200%; animation: gradient 2s ease-in-out infinite; } @keyframes gradient { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }
爆米花的加载状态是非常明显的,会伤害用户体验。
幸运的是,Suspense 是一个很棒的新特性,它为我们在Vue应用程序中协调加载状态提供了很多选择。
然而,在写这篇文章的时候,Suspense仍然被认为是实验性的,所以要谨慎行事。关于它的状态的最新信息,请参考文档。
以上是詳解Vue3 Suspense:是什麼?能幹什麼?如何用?的詳細內容。更多資訊請關注PHP中文網其他相關文章!