Appearance
Suspense
Chức Năng Thử Nghiệm
<Suspense>
là một tính năng thử nghiệm. Không đảm bảo đạt trạng thái ổn định và API có thể thay đổi trước khi nó đạt trạng thái ổn định.
<Suspense>
là một thành phần tích hợp để điều phối các phụ thuộc không đồng bộ trong cây thành phần. Nó có thể hiển thị trạng thái loading trong khi đợi các phụ thuộc không đồng bộ lồng nhau xuống cây thành phần được giải quyết.
Phụ Thuộc Không Đồng Bộ
Để giải thích vấn đề mà <Suspense>
đang cố gắng giải quyết và cách nó tương tác với các phụ thuộc không đồng bộ này, hãy tưởng tượng một cây thành phần như sau:
<Suspense>
└─ <Dashboard>
├─ <Profile>
│ └─ <FriendStatus> (component with async setup())
└─ <Content>
├─ <ActivityFeed> (async component)
└─ <Stats> (async component)
Trong cây thành phần này có nhiều thành phần lồng nhau mà việc hiển thị của chúng phụ thuộc vào việc giải quyết trước một nguồn tài nguyên không đồng bộ. Mà không có <Suspense>
, mỗi thành phần này sẽ cần xử lý trạng thái loading/lỗi và trạng thái đã nạp của nó. Trong trường hợp xấu nhất, có thể thấy ba biểu tượng quay khiến trang web có nội dung được hiển thị vào thời điểm khác nhau.
Thành phần <Suspense>
cho phép chúng ta hiển thị trạng thái loading/lỗi ở cấp độ cao nhất trong khi chờ đợi các phụ thuộc không đồng bộ lồng nhau này được giải quyết.
Có hai loại phụ thuộc không đồng bộ mà <Suspense>
có thể đợi:
Các thành phần có
setup()
không đồng bộ. Điều này bao gồm các thành phần sử dụng<script setup>
với các biểu thứcawait
ở cấp độ cao.
setup()
không đồng bộ
setup()
của một thành phần sử dụng API Composition có thể làm cho thành phần trở nên không đồng bộ:
js
export default {
async setup() {
const res = await fetch(...)
const posts = await res.json()
return {
posts
}
}
}
Nếu sử dụng <script setup>
, sự hiện diện của biểu thức await
ở cấp độ cao sẽ tự động khiến cho thành phần trở thành phụ thuộc không đồng bộ:
vue
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>
<template>
{{ posts }}
</template>
Các Thành Phần Không Đồng Bộ
Các thành phần không đồng bộ mặc định là "có thể trì hoãn". Điều này có nghĩa là nếu nó có một <Suspense>
trong chuỗi cha, nó sẽ được xem xét là một phụ thuộc không đồng bộ của <Suspense>
. Trong trường hợp này, trạng thái loading sẽ được kiểm soát bởi <Suspense>
, và các tùy chọn loading, error, delay và timeout của chính nó sẽ bị bỏ qua.
Các thành phần không đồng bộ có thể chọn không tham gia kiểm soát của Suspense
và để thành phần luôn kiểm soát trạng thái loading của mình bằng cách chỉ định suspensible: false
trong các tùy chọn của nó.
Trạng Thái Loading
Thành phần <Suspense>
có hai khe: #default
và #fallback
. Cả hai khe chỉ cho phép một nút con ngay lập tức. Nút trong khe mặc định sẽ được hiển thị nếu có thể. Nếu không, nút trong khe fallback sẽ được hiển thị thay thế.
template
<Suspense>
<!-- thành phần có các phụ thuộc không đồng bộ lồng nhau -->
<Dashboard />
<!-- trạng thái loading qua khe #fallback -->
<template #fallback>
Loading...
</template>
</Suspense>
Khi render ban đầu, <Suspense>
sẽ render nội dung của khe mặc định trong bộ nhớ. Nếu bất kỳ phụ thuộc không đồng bộ nào được gặp phải trong quá trình này, nó sẽ vào trạng thái pending. Trong trạng thái pending, nội dung của khe fallback sẽ được hiển thị. Khi tất cả các phụ thuộc không đồng bộ đã được giải quyết, <Suspense>
đi vào trạng thái resolved và nội dung của khe mặc định đã giải quyết sẽ được hiển thị.
Nếu không có phụ thuộc không đồng
bộ nào được gặp phải trong quá trình render ban đầu, <Suspense>
sẽ trực tiếp chuyển vào trạng thái resolved.
Sau khi ở trạng thái resolved, <Suspense>
sẽ chỉ quay lại trạng thái pending nếu nút gốc của khe #default
bị thay thế. Các phụ thuộc không đồng bộ mới nằm sâu trong cây sẽ không làm cho <Suspense>
quay lại trạng thái pending.
Khi một việc quay lại xảy ra, nội dung fallback sẽ không được hiển thị ngay lập tức. Thay vào đó, <Suspense>
sẽ hiển thị nội dung #default
trước đó trong khi chờ đợi nội dung mới và các phụ thuộc không đồng bộ của nó được giải quyết. Hành vi này có thể được cấu hình bằng prop timeout
: <Suspense>
sẽ chuyển sang nội dung fallback nếu mất nhiều thời gian hơn timeout
để hiển thị nội dung mặc định mới. Giá trị timeout
là 0
sẽ khiến cho nội dung fallback được hiển thị ngay lập tức khi nội dung mặc định được thay thế.
Sự Kiện
Thành phần <Suspense>
phát ra 3 sự kiện: pending
, resolve
và fallback
. Sự kiện pending
xảy ra khi vào trạng thái pending. Sự kiện resolve
được phát ra khi nội dung mới đã được giải quyết trong khe default
. Sự kiện fallback
được phát ra khi nội dung của khe fallback
được hiển thị.
Sự kiện có thể được sử dụng, ví dụ, để hiển thị một chỉ số loading trước DOM cũ trong khi các thành phần mới đang tải.
Xử Lý Lỗi
<Suspense>
hiện tại không cung cấp xử lý lỗi thông qua chính thành phần - tuy nhiên, bạn có thể sử dụng tùy chọn errorCaptured
hoặc hook onErrorCaptured()
để bắt và xử lý lỗi không đồng bộ trong thành phần cha của <Suspense>
.
Kết Hợp với Các Thành Phần Khác
Thường xuyên muốn sử dụng <Suspense>
cùng với các thành phần <Transition>
và <KeepAlive>
. Thứ tự lồng của các thành phần này là quan trọng để có thể hoạt động đúng cách.
Ngoài ra, các thành phần này thường được sử dụng cùng với thành phần <RouterView>
từ Vue Router.
Ví dụ sau đây cho thấy cách lồng ghép các thành phần này để chúng hoạt động như mong đợi. Đối với các kết hợp đơn giản hơn, bạn có thể loại bỏ các thành phần không cần thiết:
template
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- nội dung chính -->
<component :is="Component"></component>
<!-- trạng thái loading -->
<template #fallback>
Loading...
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
Vue Router có sự hỗ trợ tích hợp sẵn cho việc tải thành phần theo yêu cầu bằng cách sử dụng các import động. Các thành phần này khác biệt so với các thành phần không đồng bộ và hiện tại chúng sẽ không kích hoạt <Suspense>
. Tuy nhiên, chúng vẫn có thể có các thành phần không đồng bộ con và những thành phần đó có thể kích hoạt <Suspense>
theo cách thông thường.