<ClientOnly>Nuxt 3 提供了一個名為 <ClientOnly> 的元件,用於明確控制為客戶端渲染行為,確保此元件內的元素或內容僅在客戶端呈現,這有助於提升性能並增強使用者體驗。
// app.vue
<template>
<Sidebar />
<ClientOnly>
<Comments />
</ClientOnly>
</template>
<ClientOnly>:SSR + CSR// components/Sidebar.vue
<template>
<div>
<h1>Sidebar is both SSR and CSR</h1>
</div>
</template>
<script setup lang="ts">
// console.log('<Sidebar>', window)
</script>
<ClientOnly> 內:只有 CSR// components/Comments.vue
<template>
<div>
<h1>Comments is CSR</h1>
</div>
</template>
<script setup lang="ts">
// console.log('<Comments>', window)
</script>
我們分別插入 <Sidebar> 和 <Comments>,並將 <Comments> 包在 <ClientOnly> 內。

存在 <Sidebar /> 的內容 "Sidebar is both SSR and CSR"。

不存在 <ClientOnly> 的內容 "Comments is CSR"。

window 物件差異上方 Comments.vue 及 Sidebar.vue 的 console.log(window) 註解打開。
在 Comments.vue 中 console.log(window) 可使用。

在 Sidebar.vue 中 console.log(window) 不可使用,會直接報錯。

因 window 物件僅在瀏覽器中存在,但可透過 Vue 的 Lifecycle Hooks 指定呼叫的時間,如 onMounted() 呼叫 windows,因此 window 只在客戶端被呼叫。
<template>
<div>
<h1>Sidebar is both SSR and CSR</h1>
</div>
</template>
<script setup lang="ts">
onMounted(()=>{
console.log('<Sidebar>', window)
})
</script>

<ClientOnly> 提供了預設的 slot,當 <ClientOnly> 元件還沒在客戶端掛載時,如果希望顯示一些預設的內容避免空白頁面可以使用 fallbackTag + fallback 或 <template #fallback> ... </template> 來實現:
fallbackTag + fallback
// app.vue
<template>
<Sidebar />
<ClientOnly fallbackTag="div" fallback="Loading ...">
<Comments />
</ClientOnly>
</template>

fallbackTag="div" 的作用為 <ClientOnly> 的子組件 <Comments /> 尚未在客戶端渲染完成之前,在伺服器端渲染一個 <div>Loading ...</div> 元素作為替代;若沒有指定 fallbackTag 則只會渲染一個空的 <span>。
<template #fallback> ... </template>
// app.vue
<template>
<Sidebar />
<ClientOnly>
<Comments />
<template #fallback>
<h1>加速等待 ... </h1>
<img src="https://attach.setn.com/newsimages/2019/09/20/2140660-PH.jpg" />
</template>
</ClientOnly>
</template>

加在客戶端渲染前的預設畫面,效果如同方法一;但使用 slot 的方式則可以插入更多且更有彈性的內容。
檢視網頁原始碼比較結果

⚠️如果同時使用 fallbackTag 和 <template #fallback> ... </template>,只會渲染 <template #fallback> ... </template> 的內容。
如果想使用其他自訂 Components 作為預設畫面,可使用 <template #fallback> <SomeComponent /> </template>;無法使用 fallbackTag 達成(僅能放入 HTML Tag)。
.client & .server除了可以透過上述的 <clientOnly> 來指定特定元素僅在客戶端渲染之外,還可以透過 Components 名稱加上後綴 .client;而相對的,若是加上後綴 .server 的元件則會僅在伺服器端渲染。
分別建立 components/Sidebar.server.vue 和 components/Comments.client.vue
// app.vue
<template>
<Sidebar />
<Comments />
</template>
接連幾天介紹 Nuxt 3 的各種函式和元件,明天開始會介紹 Nuxt 3 的 Pages 和 Routing,敬請期待!
Components Directory
ClientOnly