EP28 - 非同步組件

更新於 發佈於 閱讀時間約 20 分鐘
Async Components 非同步我是知道這個東西!
是讓加載組件的時候可以非同步,改善用戶體驗吧~?
這裡應該不包含資料的非同步加載吧!?~

基本用法 - Basic Usage

在大型應用中,我們可能需要將應用劃分為較小的部分,並且僅在需要時從伺服器加載組件。為了實現這一點,Vue 提供了 defineAsyncComponent 函數:

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
// ...從伺服器加載組件
resolve(/* 加載的組件 */)
})
})
// ...像正常組件一樣使用 `AsyncComp`

如你所見,defineAsyncComponent 接受一個加載函數,該函數返回一個 Promise。當你從伺服器獲取到組件定義時,應該調用 Promise 的 resolve 回調。如果加載失敗,也可以調用 reject(reason) 來指示加載失敗。

ES 模塊的動態導入也會返回一個 Promise,因此大多數情況下,我們會將它與 defineAsyncComponent 結合使用。像 Vite 和 webpack 這樣的打包工具也支持該語法(並會將其作為捆綁分割點),因此我們可以用它來導入 Vue 單文件組件(SFC):

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)

結果的 AsyncComp 是一個包裝組件,只有在實際渲染在頁面上時才會調用加載函數。此外,它會將任何屬性和插槽傳遞給內部組件,因此你可以使用這個非同步包裝器無縫地替換原始組件,同時實現延遲加載。

與普通組件一樣,可以使用 app.component() 全局註冊非同步組件:

app.component('MyComponent', defineAsyncComponent(() =>
import('./components/MyComponent.vue')
))

也可以直接在其父組件內部定義:

<script setup>
import { defineAsyncComponent } from 'vue'

const AdminPage = defineAsyncComponent(() =>
import('./components/AdminPageComponent.vue')
)
</script>

<template>
<AdminPage />
</template>

Q: 什麼是Promise?

Promise 是 JavaScript 中的一個對象,用於表示一個異步操作的最終完成(或失敗)及其結果值。簡單來說,Promise 可以讓你在執行異步操作時更方便地處理結果,尤其是在處理異步請求時(如 AJAX 請求)。

Promise 的狀態

Promise 具有三種狀態:

  1. 待定(Pending):初始狀態,既不是成功也不是失敗。
  2. 已解決(Fulfilled):異步操作成功完成,並返回了一個結果。
  3. 已拒絕(Rejected):異步操作失敗,並返回了一個錯誤原因。

使用 Promise 的基本範例

以下是一個使用 Promise 的簡單範例:

const myPromise = new Promise((resolve, reject) => {
// 假設這是一個異步操作,例如 AJAX 請求
const success = true; // 模擬操作成功或失敗的標誌

if (success) {
resolve("操作成功!"); // 異步操作成功,調用 resolve
} else {
reject("操作失敗!"); // 異步操作失敗,調用 reject
}
});

// 使用 then() 處理已解決的狀態,catch() 處理已拒絕的狀態
myPromise
.then(result => {
console.log(result); // 輸出: 操作成功!
})
.catch(error => {
console.error(error); // 如果失敗,輸出錯誤原因
});

Promise 的鏈式調用

Promise 允許鏈式調用,即在一個 Promise 完成後可以再返回一個新的 Promise,這樣可以方便地處理多個異步操作。例如:

fetch('https://api.example.com/data')
.then(response => response.json()) // 將 response 轉換為 JSON
.then(data => {
console.log(data); // 使用獲取到的數據
})
.catch(error => {
console.error('發生錯誤:', error); // 處理錯誤
});

這段程式碼實際上是使用 fetch API,它本身就會返回一個 Promise 物件。這裡是 Promise 的運作方式的具體說明:

fetch 函數

fetch 函數用於發送網絡請求,它會返回一個 Promise。這個 Promise 代表請求的狀態,會在請求完成後進行解決(resolve)或拒絕(reject)。

總結

Promise 使得處理異步操作更加簡潔和可讀,尤其是在需要執行多個連續的異步操作時。使用 Promise 可以避免回調地獄(callback hell)現象,使代碼更易於維護和理解。

加載和錯誤狀態 - Loading and Error States

非同步操作必然涉及加載和錯誤狀態——defineAsyncComponent() 支持通過高級選項處理這些狀態:

const AsyncComp = defineAsyncComponent({
// 加載函數
loader: () => import('./Foo.vue'),

// 加載非同步組件時使用的組件
loadingComponent: LoadingComponent,
// 顯示加載組件的延遲。默認:200ms。
delay: 200,

// 加載失敗時使用的組件
errorComponent: ErrorComponent,
// 如果提供並超過超時,將顯示錯誤組件。默認:Infinity。
timeout: 3000
})

如果提供了加載組件,則在內部組件加載時會首先顯示它。顯示加載組件之前會有一個默認的 200 毫秒延遲,這是因為在快速網絡下,瞬時的加載狀態可能會被替換得太快,最終看起來像是閃爍。

如果提供了錯誤組件,當加載函數返回的 Promise 被拒絕時,它會顯示。如果請求花費的時間過長,你還可以指定超時,以顯示錯誤組件。

Q: 什麼是Hydration 水合?

水合(Hydration) 是一個在伺服器端渲染(Server-Side Rendering, SSR)應用程式中使用的概念,特別是在像 Vue.js 和 React 這樣的框架中。這個過程涉及將伺服器生成的靜態 HTML 轉換為可互動的客戶端應用程式。

水合的工作流程

  1. 伺服器端渲染:當用戶請求一個頁面時,伺服器生成一個完整的 HTML 頁面並發送給用戶的瀏覽器。這樣用戶可以快速看到內容,而不必等待 JavaScript 完全加載。
  2. 客戶端加載 JavaScript:當瀏覽器接收到 HTML 時,JavaScript 檔案也會被加載。這些 JavaScript 代碼包含了應用程式的邏輯和行為。
  3. 添加互動性:在 JavaScript 加載完成後,框架會將伺服器生成的靜態內容「水合」成可互動的元素。這包括將事件處理器綁定到 DOM 元素,使得用戶可以與應用程式互動。

水合的好處

  • 快速響應時間:用戶可以立即看到頁面內容,而不必等待所有 JavaScript 完全加載。這提高了首次渲染的速度。
  • SEO 友好:伺服器端渲染的頁面對搜尋引擎更友好,因為搜尋引擎可以抓取到靜態 HTML 內容,而不是等待 JavaScript 加載。
  • 增強用戶體驗:用戶在等待 JavaScript 加載時,仍然能夠看到和訪問內容,提供更流暢的體驗。

水合的挑戰

  • 狀態不一致:如果伺服器端渲染的 HTML 和客戶端的 JavaScript 之間存在狀態不一致,可能會導致重水合過程中的錯誤或不正確的顯示。
  • 性能開銷:水合過程會消耗資源,特別是在需要大量 DOM 操作的情況下。

延遲水合 - Lazy Hydration (3.5+)

這部分僅適用於使用伺服器端渲染的情況。

在 Vue 3.5+ 中,非同步組件可以通過提供水合策略來控制何時水合。

Vue 提供了多種內建水合策略。這些內建策略需要單獨導入,以便在未使用時進行樹搖(tree-shaking)。

設計上是故意低層次的,以提高靈活性。將來可以在核心或更高層次的解決方案(例如 Nuxt)上構建編譯器語法糖。

空閒時水合 - Hydrate on Idle

通過 requestIdleCallback 進行水合:

import { defineAsyncComponent, hydrateOnIdle } from 'vue'

const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnIdle(/* 可選地傳入最大超時 */)
})

可見時水合 - Hydrate on Visible

當元素變得可見時通過 IntersectionObserver 進行水合。

import { defineAsyncComponent, hydrateOnVisible } from 'vue'

const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnVisible()
})

可以選擇性地傳入觀察者的選項對象值:

hydrateOnVisible({ rootMargin: '100px' })

媒體查詢時水合 - Hydrate on Media Query

當指定的媒體查詢匹配時進行水合。

import { defineAsyncComponent, hydrateOnMediaQuery } from 'vue'

const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnMediaQuery('(max-width:500px)')
})

交互時水合 - Hydrate on Interaction

當在組件元素上觸發指定事件時進行水合。觸發水合的事件也會在水合完成後重放。

import { defineAsyncComponent, hydrateOnInteraction } from 'vue'

const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnInteraction('click')
})

也可以是多個事件類型的列表:

hydrateOnInteraction(['wheel', 'mouseover'])

自定義策略 - Custom Strategy

import { defineAsyncComponent, type HydrationStrategy } from 'vue'

const myStrategy: HydrationStrategy = (hydrate, forEachElement) => {
// forEachElement 是一個幫助函數,用於迭代組件的非水合 DOM 中的所有根元素,
// 因為根元素可以是片段而不是單一元素
forEachElement(el => {
// ...
})
// 準備好後調用 `hydrate`
hydrate()
return () => {
// 如果需要,返回一個清理函數
}
}

const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: myStrategy
})

與 Suspense 一起使用 - Using with Suspense

非同步組件可以與 <Suspense> 內建組件一起使用。<Suspense> 和非同步組件之間的交互已在專門的 <Suspense> 章節中記錄。

Q: 什麼是Suspense?

Suspense 是 Vue 3 中引入的一個功能,用於處理異步組件的加載狀態。它允許開發者更輕鬆地管理異步組件在加載過程中的顯示效果,例如在組件尚未加載完成時顯示一個加載指示器或佔位符。

Suspense 的工作原理

  1. 包裹異步組件:將一個或多個異步組件包裹在 <Suspense> 組件中,這樣可以控制當這些異步組件加載時的顯示內容。
  2. 使用 slot<Suspense> 提供了兩個 slot:
  • default slot:用於顯示加載完成後的內容。
  • fallback slot:用於顯示在加載期間的內容,例如加載指示器或佔位符。

以下是一個使用 Suspense 的範例:

<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<LoadingSpinner /> <!-- 加載時顯示的內容 -->
</template>
</Suspense>
</template>

<script setup>
import AsyncComponent from './AsyncComponent.vue'
import LoadingSpinner from './LoadingSpinner.vue'
</script>

在這個例子中,當 AsyncComponent 正在加載時,<LoadingSpinner /> 將顯示在畫面上。一旦 AsyncComponent 加載完成,<AsyncComponent /> 將取代加載指示器顯示。

使用場景

  • 異步數據加載:當需要從伺服器獲取數據並且這個過程可能需要一些時間時,可以使用 Suspense 來提供良好的用戶體驗。
  • 多個異步組件:當有多個異步組件需要加載時,可以使用 Suspense 來統一管理它們的加載狀態。

結論

Suspense 是 Vue 3 提供的一個強大功能,旨在簡化異步組件的管理和顯示。它可以提升用戶體驗,讓開發者更方便地處理異步加載時的狀態和顯示效果。

Q: 為什麼要用SSR?

使用 伺服器端渲染(SSR, Server-Side Rendering) 有多個優點,特別是在構建現代化的網頁應用程式時。以下是一些使用 SSR 的原因:

1. 更快的初始加載時間

  • SSR 會在伺服器上預渲染 HTML,這樣用戶在首次加載頁面時可以立即看到內容,而不需要等待 JavaScript 加載和執行完成。這對於用戶體驗尤為重要,特別是在網速較慢的情況下。

2. 更好的 SEO(搜索引擎優化)

  • 搜索引擎的爬蟲在索引網頁內容時通常對 JavaScript 渲染的內容支持不佳。使用 SSR,頁面的 HTML 在伺服器端生成,搜索引擎可以輕易讀取並索引這些內容,從而提高網站的可見性和排名。

3. 改善社交媒體分享

  • 在 SSR 中生成的 HTML 包含完整的內容,這樣當用戶在社交媒體上分享頁面鏈接時,社交平台能夠抓取到正確的預覽信息(例如標題、描述、縮略圖等),提高了分享的效果。

4. 降低客戶端負擔

  • SSR 將大部分渲染工作移至伺服器端,這可以降低客戶端設備(特別是移動設備)的運算負擔,提供更流暢的用戶體驗。

5. 更佳的性能優化

  • SSR 使得在伺服器端進行性能優化變得更加容易,例如使用快取來提高響應速度,這對於需要快速響應的應用特別重要。

6. 支持多種渲染策略

  • 使用 SSR 可以更靈活地支持不同的渲染策略,例如先渲染主要內容,然後再進行其他次要內容的加載。

7. 可擴展性

  • SSR 可以輕鬆地與其他技術堆棧結合使用,例如與 Node.js、Express、Django 等後端框架協作,提供更完整的全棧解決方案。

總結

SSR 提供了更好的性能、SEO 和用戶體驗,使其成為構建現代網頁應用程式的一個非常有效的解決方案。然而,實現 SSR 也需要考慮一些挑戰,例如伺服器負載和複雜的開發過程,因此在選擇使用 SSR 時需要根據具體需求進行評估。

這章節有些JS沒學好的觀念一起補齊,
這樣才能對框架的非同步能有更深的理解www~
文章中很多Q的話~就是我個人的疑問,
盡量找好理解的資訊放上去了,難道大家都這麼清楚嗎?
Vue + Nuxt.js = SSR









留言
avatar-img
留言分享你的想法!
avatar-img
卡關的人生
2會員
73內容數
分享生活趣事~
卡關的人生的其他內容
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
看更多
你可能也想看
Thumbnail
孩子寫功課時瞇眼?小心近視!這款喜光全光譜TIONE⁺光健康智慧檯燈,獲眼科院長推薦,網路好評不斷!全光譜LED、180cm大照明範圍、5段亮度及色溫調整、350度萬向旋轉,讓孩子學習更舒適、保護眼睛!
Thumbnail
孩子寫功課時瞇眼?小心近視!這款喜光全光譜TIONE⁺光健康智慧檯燈,獲眼科院長推薦,網路好評不斷!全光譜LED、180cm大照明範圍、5段亮度及色溫調整、350度萬向旋轉,讓孩子學習更舒適、保護眼睛!
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
Composition API 是 Vue 3 的一組 API,允許使用導入的函數編寫 Vue 組件,涵蓋反應性 API、生命週期鉤子和依賴注入。它主要在單文件組件的 <script setup> 語法中使用。
Thumbnail
Composition API 是 Vue 3 的一組 API,允許使用導入的函數編寫 Vue 組件,涵蓋反應性 API、生命週期鉤子和依賴注入。它主要在單文件組件的 <script setup> 語法中使用。
Thumbnail
<Suspense> 是 Vue 3 的一個實驗性組件,用於協調異步組件的加載狀態,簡化異步處理。它能在等待多個嵌套組件的異步依賴解決時顯示加載指示器,避免每個組件獨立處理加載和錯誤狀態,從而提高用戶體驗。
Thumbnail
<Suspense> 是 Vue 3 的一個實驗性組件,用於協調異步組件的加載狀態,簡化異步處理。它能在等待多個嵌套組件的異步依賴解決時顯示加載指示器,避免每個組件獨立處理加載和錯誤狀態,從而提高用戶體驗。
Thumbnail
學會了使用 <KeepAlive>,這是一個內建組件,允許我們在動態切換多個組件時有條件地緩存組件實例,避免狀態丟失並提高速度。使用 include 和 exclude 屬性來自定義緩存行為,並透過 max 屬性限制緩存的實例數量。
Thumbnail
學會了使用 <KeepAlive>,這是一個內建組件,允許我們在動態切換多個組件時有條件地緩存組件實例,避免狀態丟失並提高速度。使用 include 和 exclude 屬性來自定義緩存行為,並透過 max 屬性限制緩存的實例數量。
Thumbnail
Vue 插件是自包含的代碼,通過 app.use() 方法安裝,用於擴展應用功能。插件可定義為包含 install() 方法的物件,或簡單的函數。插件用途包括註冊全局組件、自定義指令、資源注入和添加全局屬性。
Thumbnail
Vue 插件是自包含的代碼,通過 app.use() 方法安裝,用於擴展應用功能。插件可定義為包含 install() 方法的物件,或簡單的函數。插件用途包括註冊全局組件、自定義指令、資源注入和添加全局屬性。
Thumbnail
使用 defineAsyncComponent 函數可實現此功能,它接受一個返回 Promise 的加載函數。在大型應用中,組件可以按需加載,並且可與 ES 模塊的動態導入結合使用。還可以使用高級選項處理加載和錯誤狀態,例如設置加載組件和超時設定。
Thumbnail
使用 defineAsyncComponent 函數可實現此功能,它接受一個返回 Promise 的加載函數。在大型應用中,組件可以按需加載,並且可與 ES 模塊的動態導入結合使用。還可以使用高級選項處理加載和錯誤狀態,例如設置加載組件和超時設定。
Thumbnail
通過使用插槽,組件可以變得更加靈活和可重用,例如 <FancyButton> 可以讓父組件提供按鈕的內部內容,而 <BaseLayout> 則可以使用命名插槽來指定不同區域的內容。插槽還可以設置預設內容,當父組件未提供插槽內容時,會渲染預設內容。
Thumbnail
通過使用插槽,組件可以變得更加靈活和可重用,例如 <FancyButton> 可以讓父組件提供按鈕的內部內容,而 <BaseLayout> 則可以使用命名插槽來指定不同區域的內容。插槽還可以設置預設內容,當父組件未提供插槽內容時,會渲染預設內容。
Thumbnail
這次可以將範例內容轉移到components資料夾中,並在App.vue中引用它們。在App.vue中使用import引入其他組件。新範例中定義了兩個函數:一個用於反轉字串,另一個用於彈出警告框。透過按鈕點擊可觸發這些功能。這些範例簡單易懂,實作會比閱讀文件更有效率!
Thumbnail
這次可以將範例內容轉移到components資料夾中,並在App.vue中引用它們。在App.vue中使用import引入其他組件。新範例中定義了兩個函數:一個用於反轉字串,另一個用於彈出警告框。透過按鈕點擊可觸發這些功能。這些範例簡單易懂,實作會比閱讀文件更有效率!
Thumbnail
歡迎來到 React 白話文運動第三篇,今天我們將深入探討 JavaScript 中的非同步操作,並介紹兩個重要的關鍵字:Async 與 Await。在這篇文章中,我們將透過實例演示非同步操作的概念,以及如何使用 Promise、Fetch、Async 和 Await 來更有效地處理非同步程式。
Thumbnail
歡迎來到 React 白話文運動第三篇,今天我們將深入探討 JavaScript 中的非同步操作,並介紹兩個重要的關鍵字:Async 與 Await。在這篇文章中,我們將透過實例演示非同步操作的概念,以及如何使用 Promise、Fetch、Async 和 Await 來更有效地處理非同步程式。
Thumbnail
JavaScript 中的 ESM(ES Modules)和 CJS(CommonJS)是用於模塊化開發的兩種不同的模組系統。 關於CJS CJS 是 CommonJS 的模塊系統,最初是為了在伺服器端使用的 Node.js 開發而設計的,但也被廣泛用於前端開發。CJS 使用 require 函數來
Thumbnail
JavaScript 中的 ESM(ES Modules)和 CJS(CommonJS)是用於模塊化開發的兩種不同的模組系統。 關於CJS CJS 是 CommonJS 的模塊系統,最初是為了在伺服器端使用的 Node.js 開發而設計的,但也被廣泛用於前端開發。CJS 使用 require 函數來
Thumbnail
promise是ES6才有的,它是一種非同步的技術,使用它除了可以在background處理一些事情以外,還可以增加程式碼的可維護性。
Thumbnail
promise是ES6才有的,它是一種非同步的技術,使用它除了可以在background處理一些事情以外,還可以增加程式碼的可維護性。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News