EP44 - 懸掛

閱讀時間約 12 分鐘
Suspense,這也是新觀念~
但應該跟異步有點關係吧?
快來看看吧!
每天看一篇文章~希望能持續下去啊!
大家會想什麼內容呢?快來留言吧

實驗性功能

<Suspense> 是一個實驗性功能。它不保證會達到穩定狀態,並且 API 在達到穩定狀態之前可能會發生變化。

<Suspense> 是一個內建組件,用於在組件樹中協調異步依賴。它可以在等待組件樹中的多個嵌套異步依賴解決時,渲染一個加載狀態。

異步依賴 - Async Dependencies​

為了解釋 <Suspense> 嘗試解決的問題以及它如何與這些異步依賴進行互動,讓我們想像一個如下的組件層次結構:

<Suspense>
└─ <Dashboard>
├─ <Profile>
│ └─ <FriendStatus> (具有異步 setup() 的組件)
└─ <Content>
├─ <ActivityFeed> (異步組件)
└─ <Stats> (異步組件)

在這個組件樹中,有多個嵌套組件的渲染依賴於首先解決一些異步資源。沒有 <Suspense> 的情況下,每個組件都需要處理自己的加載/錯誤和已加載狀態。在最糟糕的情況下,我們可能會在頁面上看到三個加載旋轉器,並且內容會在不同的時間顯示出來。

<Suspense> 組件使我們能夠在等待這些嵌套異步依賴解決時,顯示頂層的加載/錯誤狀態。

<Suspense> 可以等待兩種類型的異步依賴:

  1. 具有異步 setup() 鉤子的組件。這包括使用 <script setup> 並帶有頂層 await 表達式的組件。
  2. 異步組件

異步 setup() - async setup()

Composition API 組件的 setup() 鉤子可以是異步的:

export default {
async setup() {
const res = await fetch(...)
const posts = await res.json()
return {
posts
}
}
}

如果使用 <script setup>,頂層 await 表達式的存在會自動使組件成為異步依賴:

<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>

<template>
{{ posts }}
</template>

異步組件 - Async Components

異步組件默認是“可暫停”的。這意味著如果它在父鏈中有 <Suspense>,它將被視為該 <Suspense> 的異步依賴。在這種情況下,加載狀態將由 <Suspense> 控制,而組件本身的加載、錯誤、延遲和超時選項將被忽略。

異步組件可以通過在其選項中指定 suspensible: false 來選擇不受 <Suspense> 控制,並讓組件始終控制其自己的加載狀態。

加載狀態 - Loading State

<Suspense> 組件有兩個插槽:#default#fallback。這兩個插槽都只允許一個直接的子節點。如果可能,將顯示默認插槽中的節點。如果不可能,將顯示回退插槽中的節點。

<template>
<Suspense>
<!-- 具有嵌套異步依賴的組件 -->
<Dashboard />

<!-- 通過 #fallback 插槽顯示加載狀態 -->
<template #fallback>
Loading...
</template>
</Suspense>
</template>

在初始渲染時,<Suspense> 會在內存中渲染其默認插槽內容。如果在此過程中遇到任何異步依賴,它將進入等待狀態。在等待狀態期間,將顯示回退內容。當所有遇到的異步依賴解決後,<Suspense> 進入已解決狀態,並顯示已解決的默認插槽內容。

如果在初始渲染期間沒有遇到異步依賴,<Suspense> 將直接進入已解決狀態。

一旦進入已解決狀態,只有在 #default 插槽的根節點被替換時,<Suspense> 才會恢復到等待狀態。嵌套在樹中更深處的新異步依賴不會導致 <Suspense> 恢復到等待狀態。

當發生恢復時,不會立即顯示回退內容。相反,<Suspense> 將顯示之前的 #default 內容,同時等待新內容及其異步依賴被解決。這種行為可以通過 timeout 屬性配置:如果渲染新默認內容花費的時間超過 timeout<Suspense> 將切換到回退內容。timeout 值為 0 時,當默認內容被替換時,將立即顯示回退內容。

事件 - Events

<Suspense> 組件會觸發三個事件:pendingresolvefallbackpending 事件在進入等待狀態時觸發。resolve 事件在默認插槽的新內容解析完成時觸發。fallback 事件在顯示回退插槽內容時觸發。

這些事件可以用來在新組件加載時,在舊的 DOM 前顯示加載指示器。

錯誤處理 - Error Handling​

<Suspense> 目前不通過自身組件提供錯誤處理功能——然而,你可以使用 errorCaptured 選項或 onErrorCaptured() 鉤子在 <Suspense> 的父組件中捕獲並處理異步錯誤。

結合其他組件 - Combining with Other Components​

通常會希望將 <Suspense><Transition><KeepAlive> 組件結合使用。這些組件的嵌套順序對於正確運行它們非常重要。

此外,這些組件經常與 Vue Router 的 <RouterView> 組件一起使用。

下面的範例展示了如何嵌套這些組件,以便它們都能按預期運行。對於較簡單的組合,可以移除不需要的組件:

<template>
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- 主內容 -->
<component :is="Component"></component>

<!-- 加載狀態 -->
<template #fallback>
Loading...
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
</template>

Vue Router 支援使用動態導入來延遲加載組件。這些與異步組件不同,且目前不會觸發 <Suspense>。然而,它們仍然可以有異步組件作為後代,並且這些後代可以按照通常方式觸發 <Suspense>

嵌套的 Suspense - Nested Suspense​

僅支援於 3.3+

當我們有多個異步組件(常見於嵌套或基於佈局的路由)時,如下所示:

<template>
<Suspense>
<component :is="DynamicAsyncOuter">
<component :is="DynamicAsyncInner" />
</component>
</Suspense>
</template>

<Suspense> 會如預期地創建一個邊界,解決樹狀結構中的所有異步組件。然而,當我們更改 DynamicAsyncOuter 時,<Suspense> 會正確等待,但當我們更改 DynamicAsyncInner 時,嵌套的 DynamicAsyncInner 會在解析完成之前渲染一個空節點(而不是先前的節點或 fallback 插槽)。

為了解決這個問題,我們可以使用嵌套的 suspense 來處理嵌套組件的補丁,如下所示:

<template>
<Suspense>
<component :is="DynamicAsyncOuter">
<Suspense suspensible> <!-- 這裡 -->
<component :is="DynamicAsyncInner" />
</Suspense>
</component>
</Suspense>
</template>

如果不設置 suspensible 屬性,內部的 <Suspense> 會被父級 <Suspense> 視為同步組件。這意味著它有自己的 fallback 插槽,如果兩個動態組件同時變更,可能會出現空節點和多個補丁週期,而子級 <Suspense> 正在加載其自己的依賴樹,這可能是不理想的。設置 suspensible 屬性後,所有的異步依賴處理將交給父級 <Suspense>(包括觸發的事件),而內部的 <Suspense> 僅作為另一個依賴解析和補丁的邊界。

相關文件

看完這些文章還是沒有很懂www
應該就是應用在以下情境中

是的,<Suspense> 的主要應用之一是處理加載狀態,但它的用途不僅限於加載器。以下是一些 <Suspense> 可以用於的場景:

  1. 異步數據獲取:
    • 當一個組件需要從 API 獲取數據時,可以使用 <Suspense> 來管理加載狀態,避免顯示未加載的數據。
  2. 異步路由加載:
    • 在使用 Vue Router 時,<Suspense> 可以用於處理異步路由組件的加載,這樣在導航到新路由時可以顯示加載指示器。
  3. 組件樹的異步加載:
    • 當有多個嵌套的組件需要異步加載時,<Suspense> 可以協調它們的加載,確保在所有依賴被解析之前不會顯示任何內容。
  4. 錯誤處理:
    • 雖然 <Suspense> 本身不提供錯誤處理,但可以與其他錯誤捕獲組件(如 errorCaptured)結合使用,以便在異步加載過程中捕獲並處理錯誤。
  5. 視覺效果和過渡:
    • <Transition> 組件結合使用時,可以在加載或變更組件時應用過渡效果,增強用戶體驗。
  6. 整合和組織代碼:
    • 將異步邏輯封裝在 <Suspense> 中可以幫助組織代碼,保持組件的簡潔性,讓異步邏輯更具可讀性和可維護性。

總結

<Suspense> 提供了一種優雅的方式來處理異步組件的加載和錯誤管理,無論是用於單個組件的加載,還是多個組件的嵌套加載,它都能幫助開發者改善用戶體驗。

2會員
63內容數
分享生活趣事~
留言0
查看全部
發表第一個留言支持創作者!
卡關的人生 的其他內容
<Teleport> 是 Vue.js 的內建組件,允許我們將組件的部分內容「傳送」到 DOM 的不同位置,即使這些內容邏輯上屬於某個組件,但可以在視覺上顯示在其他位置,常用於模態框和覆蓋層等情境。
學會了使用 <KeepAlive>,這是一個內建組件,允許我們在動態切換多個組件時有條件地緩存組件實例,避免狀態丟失並提高速度。使用 include 和 exclude 屬性來自定義緩存行為,並透過 max 屬性限制緩存的實例數量。
<TransitionGroup> 是一個內建組件,專為對列表中的元素或組件進行插入、移除和順序變更的動畫處理而設計。與 <Transition> 的主要區別在於,<TransitionGroup> 預設不會渲染包裹元素,且不支持轉場模式。
<Transition> 用於元素或組件進入和離開 DOM 時的動畫,而 <TransitionGroup> 則應用於 v-for 列表的插入、移除或移動。這些組件透過 CSS 類別來控制動畫,例如進入/離開的狀態類別。可以透過 name 屬性自訂過渡效果的命名。
Vue 插件是自包含的代碼,通過 app.use() 方法安裝,用於擴展應用功能。插件可定義為包含 install() 方法的物件,或簡單的函數。插件用途包括註冊全局組件、自定義指令、資源注入和添加全局屬性。
Vue 自定義指令用於擴充功能,需要直接操作 DOM 時使用。常見例子如 v-focus 指令,使元素在插入 DOM 時自動獲得焦點。指令定義物件可提供多個生命週期鉤子函數,例如 mounted、updated 等。指令可以簡寫為函數,當行為在 mounted 和 updated 時相同時特別方便。
<Teleport> 是 Vue.js 的內建組件,允許我們將組件的部分內容「傳送」到 DOM 的不同位置,即使這些內容邏輯上屬於某個組件,但可以在視覺上顯示在其他位置,常用於模態框和覆蓋層等情境。
學會了使用 <KeepAlive>,這是一個內建組件,允許我們在動態切換多個組件時有條件地緩存組件實例,避免狀態丟失並提高速度。使用 include 和 exclude 屬性來自定義緩存行為,並透過 max 屬性限制緩存的實例數量。
<TransitionGroup> 是一個內建組件,專為對列表中的元素或組件進行插入、移除和順序變更的動畫處理而設計。與 <Transition> 的主要區別在於,<TransitionGroup> 預設不會渲染包裹元素,且不支持轉場模式。
<Transition> 用於元素或組件進入和離開 DOM 時的動畫,而 <TransitionGroup> 則應用於 v-for 列表的插入、移除或移動。這些組件透過 CSS 類別來控制動畫,例如進入/離開的狀態類別。可以透過 name 屬性自訂過渡效果的命名。
Vue 插件是自包含的代碼,通過 app.use() 方法安裝,用於擴展應用功能。插件可定義為包含 install() 方法的物件,或簡單的函數。插件用途包括註冊全局組件、自定義指令、資源注入和添加全局屬性。
Vue 自定義指令用於擴充功能,需要直接操作 DOM 時使用。常見例子如 v-focus 指令,使元素在插入 DOM 時自動獲得焦點。指令定義物件可提供多個生命週期鉤子函數,例如 mounted、updated 等。指令可以簡寫為函數,當行為在 mounted 和 updated 時相同時特別方便。
你可能也想看
Google News 追蹤
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
一開始你先把你的專案push上去後,修改vite.config.ts ,要在裡面新增  base: "/Cart/" (/放自己的專案名稱/) build: {outDir: "docs"}, 接下來你要去你的github setting 裡面 -> Page ->選Deploy fro
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
Thumbnail
這是為了搭建自己想要的工作流而開始的研究工作。
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
一開始你先把你的專案push上去後,修改vite.config.ts ,要在裡面新增  base: "/Cart/" (/放自己的專案名稱/) build: {outDir: "docs"}, 接下來你要去你的github setting 裡面 -> Page ->選Deploy fro
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
Thumbnail
這是為了搭建自己想要的工作流而開始的研究工作。