透過 100 多個技巧的集合學習 Nuxt!

components

components/ 目錄是用於放置所有 Vue 組件的地方。

Nuxt 會自動導入此目錄中的所有組件 (以及您可能使用的任何模組所註冊的組件)。

目錄結構
-| components/
---| AppHeader.vue
---| AppFooter.vue
app.vue
<template>
  <div>
    <AppHeader />
    <NuxtPage />
    <AppFooter />
  </div>
</template>

組件名稱

如果您在巢狀目錄中有一個組件,例如

目錄結構
-| components/
---| base/
-----| foo/
-------| Button.vue

... 那麼組件的名稱將基於其自身的路徑目錄和檔案名稱,並刪除重複的區段。因此,組件的名稱將會是

<BaseFooButton />
為了清楚起見,我們建議組件的檔案名稱與其名稱相符。因此,在上面的範例中,您可以將 Button.vue 重新命名為 BaseFooButton.vue

如果您只想根據組件的名稱(而不是路徑)自動導入組件,則需要使用配置物件的擴展形式將 pathPrefix 選項設定為 false

nuxt.config.ts
export default 
defineNuxtConfig
({
components
: [
{
path
: '~/components',
pathPrefix
: false,
}, ], });

這會使用與 Nuxt 2 中相同的策略註冊組件。例如,~/components/Some/MyComponent.vue 將可用作 <MyComponent> 而不是 <SomeMyComponent>

動態組件

如果您想使用 Vue 的 <component :is="someComputedComponent"> 語法,您需要使用 Vue 提供的 resolveComponent 輔助函式,或直接從 #components 導入組件並將其傳遞到 is prop 中。

例如

pages/index.vue
<script setup lang="ts">
import { SomeComponent } from '#components'

const MyButton = resolveComponent('MyButton')
</script>

<template>
  <component :is="clickable ? MyButton : 'div'" />
  <component :is="SomeComponent" />
</template>
如果您使用 resolveComponent 來處理動態組件,請確保除了組件的名稱之外,不要插入任何其他內容,組件名稱必須是字串而不是變數。
觀看 Daniel Roe 關於 resolveComponent 的短片。

或者,雖然不建議,但您可以全域註冊所有組件,這將為您的所有組件建立異步區塊,並使它們在整個應用程式中可用。

  export default defineNuxtConfig({
    components: {
+     global: true,
+     dirs: ['~/components']
    },
  })

您也可以透過將一些組件放置在 ~/components/global 目錄中,或在檔案名稱中使用 .global.vue 後綴,有選擇地全域註冊一些組件。如上所述,每個全域組件都在單獨的區塊中呈現,因此請注意不要過度使用此功能。

global 選項也可以針對每個組件目錄進行設定。

動態導入

若要動態導入組件(也稱為延遲載入組件),您只需將 Lazy 字首添加到組件的名稱即可。如果組件並非總是需要,這特別有用。

透過使用 Lazy 字首,您可以延遲載入組件程式碼,直到適當的時機,這有助於最佳化您的 JavaScript 套件大小。

pages/index.vue
<script setup lang="ts">
const show = ref(false)
</script>

<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
  </div>
</template>

延遲(或惰性)水合

惰性組件非常適合控制應用程式中的區塊大小,但它們並不總是能提升運行時效能,因為除非有條件地渲染,否則它們仍然會急切地載入。在真實世界的應用程式中,某些頁面可能包含大量內容和大量組件,而且大多數時候並非所有組件都需要在頁面載入後立即互動。讓它們全部急切載入可能會對效能產生負面影響。

為了最佳化您的應用程式,您可能希望延遲某些組件的水合,直到它們可見,或者直到瀏覽器完成更重要的任務。

Nuxt 透過使用惰性(或延遲)水合來支援此功能,讓您可以控制組件何時變得可互動。

水合策略

Nuxt 提供了一系列內建的水合策略。每個惰性組件只能使用一種策略。

目前,Nuxt 的內建惰性水合僅適用於單檔案組件 (SFC),並且要求您在模板中定義 prop(而不是透過 v-bind 展開 prop 物件)。它也不適用於從 #components 的直接導入。

hydrate-on-visible

當組件在視口中變得可見時,進行水合。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-visible />
  </div>
</template>
閱讀更多關於 hydrate-on-visible 選項的資訊。
在底層,這使用 Vue 的內建 hydrateOnVisible 策略

hydrate-on-idle

當瀏覽器處於閒置狀態時,進行組件水合。如果您需要組件盡快載入,但不阻塞關鍵渲染路徑,則這很適合。

您也可以傳遞一個數字,作為最大逾時時間。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-idle />
  </div>
</template>
在底層,這使用 Vue 的內建 hydrateOnIdle 策略

hydrate-on-interaction

在指定的互動(例如,點擊、滑鼠懸停)後,進行組件水合。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-interaction="mouseover" />
  </div>
</template>

如果您不傳遞事件或事件列表,則預設為在 pointerenterfocus 上進行水合。

在底層,這使用 Vue 的內建 hydrateOnInteraction 策略

hydrate-on-media-query

當視窗符合媒體查詢時,進行組件水合。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
  </div>
</template>
在底層,這使用 Vue 的內建 hydrateOnMediaQuery 策略

hydrate-after

在指定的延遲(以毫秒為單位)後,進行組件水合。

pages/index.vue
<template>
  <div>
    <LazyMyComponent :hydrate-after="2000" />
  </div>
</template>

hydrate-when

根據布林條件進行組件水合。

pages/index.vue
<template>
  <div>
    <LazyMyComponent :hydrate-when="isReady" />
  </div>
</template>
<script setup lang="ts">
const isReady = ref(false)
function myFunction() {
  // trigger custom hydration strategy...
  isReady.value = true
}
</script>

hydrate-never

永不水合組件。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-never />
  </div>
</template>

監聽水合事件

所有延遲水合組件在水合時都會發出 @hydrated 事件。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-visible @hydrated="onHydrated" />
  </div>
</template>

<script setup lang="ts">
function onHydrate() {
  console.log("Component has been hydrated!")
}
</script>

注意事項與最佳實踐

延遲水合可以提供效能優勢,但正確使用它至關重要

  1. 優先處理視口內內容: 避免對關鍵的、首屏內容使用延遲水合。它最適合用於非立即需要的內容。
  2. 條件渲染: 當在惰性組件上使用 v-if="false" 時,您可能不需要延遲水合。您可以直接使用一般的惰性組件。
  3. 共享狀態: 請留意跨多個元件的共享狀態 (v-model)。在一個元件中更新 model 可能會觸發所有綁定到該 model 的元件進行 hydration。
  4. 善用各策略的預期用途: 每個策略都針對特定目的進行了最佳化。
    • hydrate-when 最適合可能不一定需要 hydration 的元件。
    • hydrate-after 適用於可以等待特定時間的元件。
    • hydrate-on-idle 適用於可以在瀏覽器閒置時進行 hydration 的元件。
  5. 避免在互動式元件上使用 hydrate-never:如果元件需要使用者互動,則不應設定為永不 hydration。

直接匯入

如果您想要或需要繞過 Nuxt 的自動匯入功能,您也可以從 #components 明確地匯入元件。

pages/index.vue
<script setup lang="ts">
import { NuxtLink, LazyMountainsList } from '#components'

const show = ref(false)
</script>

<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
    <NuxtLink to="/">Home</NuxtLink>
  </div>
</template>

自訂目錄

預設情況下,只會掃描 ~/components 目錄。如果您想要新增其他目錄,或變更如何掃描此目錄子資料夾中的元件,您可以將其他目錄新增至設定檔。

nuxt.config.ts
export default 
defineNuxtConfig
({
components
: [
// ~/calendar-module/components/event/Update.vue => <EventUpdate /> {
path
: '~/calendar-module/components' },
// ~/user-module/components/account/UserDeleteDialog.vue => <UserDeleteDialog /> {
path
: '~/user-module/components',
pathPrefix
: false },
// ~/components/special-components/Btn.vue => <SpecialBtn /> {
path
: '~/components/special-components',
prefix
: 'Special' },
// It's important that this comes last if you have overrides you wish to apply // to sub-directories of `~/components`. // // ~/components/Btn.vue => <Btn /> // ~/components/base/Btn.vue => <BaseBtn /> '~/components' ] })

npm 套件

如果您想要從 npm 套件自動匯入元件,您可以使用 addComponentlocal module 中註冊它們。

import { 
addComponent
,
defineNuxtModule
} from '@nuxt/kit'
export default
defineNuxtModule
({
setup
() {
// import { MyComponent as MyAutoImportedComponent } from 'my-npm-package'
addComponent
({
name
: 'MyAutoImportedComponent',
export
: 'MyComponent',
filePath
: 'my-npm-package',
}) }, })
任何巢狀目錄都需要先新增,因為它們是依序掃描的。

元件副檔名

預設情況下,任何副檔名在 nuxt.config.ts 的 extensions 鍵 中指定的檔案都會被視為元件。如果您需要限制應註冊為元件的檔案副檔名,您可以使用元件目錄宣告的擴充形式及其 extensions 鍵。

nuxt.config.ts
export default 
defineNuxtConfig
({
components
: [
{
path
: '~/components',
extensions
: ['.vue'],
} ] })

客戶端元件

如果元件僅需在客戶端渲染,您可以將 .client 後綴添加到您的元件。

目錄結構
| components/
--| Comments.client.vue
pages/example.vue
<template>
  <div>
    <!-- this component will only be rendered on client side -->
    <Comments />
  </div>
</template>
此功能僅適用於 Nuxt 自動匯入和 #components 匯入。從它們的真實路徑明確匯入這些元件不會將它們轉換為僅限客戶端的元件。
.client 元件僅在掛載後才會渲染。若要使用 onMounted() 存取渲染後的模板,請在 onMounted() hook 的回呼中新增 await nextTick()
您也可以使用 <ClientOnly> 元件達到類似的結果。

伺服器元件

伺服器元件允許在您的客戶端應用程式中伺服器渲染個別元件。即使您正在產生靜態網站,也可以在 Nuxt 中使用伺服器元件。這使得構建複雜網站成為可能,這些網站混合了動態元件、伺服器渲染的 HTML 甚至靜態標記區塊。

伺服器元件可以單獨使用,也可以與 客戶端元件 配對使用。

觀看 Learn Vue 關於 Nuxt 伺服器元件的影片。
閱讀 Daniel Roe 撰寫的 Nuxt 伺服器元件指南。

獨立伺服器元件

獨立伺服器元件將始終在伺服器上渲染,也稱為 Islands 元件。

當它們的 props 更新時,這將導致一個網路請求,該請求將就地更新渲染的 HTML。

伺服器元件目前處於實驗階段,為了使用它們,您需要在您的 nuxt.config 中啟用 'component islands' 功能。

nuxt.config.ts
export default 
defineNuxtConfig
({
experimental
: {
componentIslands
: true
} })

現在您可以使用 .server 後綴註冊僅限伺服器的元件,並在您的應用程式中的任何位置自動使用它們。

目錄結構
-| components/
---| HighlightedMarkdown.server.vue
pages/example.vue
<template>
  <div>
    <!--
      this will automatically be rendered on the server, meaning your markdown parsing + highlighting
      libraries are not included in your client bundle.
     -->
    <HighlightedMarkdown markdown="# Headline" />
  </div>
</template>

僅限伺服器的元件在底層使用 <NuxtIsland>,這意味著 lazy prop 和 #fallback slot 都會傳遞給它。

伺服器元件(和 islands)必須具有單個根元素。(HTML 註解也被視為元素。)
當在其他 islands 中巢狀 islands 時要小心,因為每個 island 都會增加一些額外開銷。
伺服器專用元件和島嶼元件的大多數功能,例如 slots 和客戶端元件,僅適用於單一檔案元件。

伺服器元件內的客戶端元件

此功能需要您的設定檔中 experimental.componentIslands.selectiveClient 為 true。

您可以通過在您希望在客戶端載入的元件上設定 nuxt-client 屬性來部分 hydration 元件。

components/ServerWithClient.vue
<template>
  <div>
    <HighlightedMarkdown markdown="# Headline" />
    <!-- Counter will be loaded and hydrated client-side -->
    <Counter nuxt-client :count="5" />
  </div>
</template>
這僅在伺服器元件內有效。客戶端元件的 Slots 僅在 experimental.componentIsland.selectiveClient 設定為 'deep' 時才有效,並且由於它們是在伺服器端渲染的,因此一旦在客戶端就無法互動。

伺服器元件上下文

當渲染僅限伺服器或 island 元件時,<NuxtIsland> 會發出一個 fetch 請求,該請求會返回一個 NuxtIslandResponse。(如果伺服器端渲染,這是一個內部請求;如果是客戶端導航渲染,您可以在網路選項卡中看到該請求。)

這表示

  • 將在伺服器端建立一個新的 Vue 應用程式,以建立 NuxtIslandResponse
  • 在渲染元件時,將建立一個新的「island 上下文」。
  • 您無法從應用程式的其餘部分存取「island 上下文」,也無法從 island 元件存取應用程式其餘部分的上下文。換句話說,伺服器元件或 island 與應用程式的其餘部分是隔離的。
  • 您的插件將在渲染 island 時再次運行,除非它們設定了 env: { islands: false }(您可以在物件語法插件中執行此操作)。

在 island 元件中,您可以通過 nuxtApp.ssrContext.islandContext 存取其 island 上下文。請注意,雖然 island 元件仍標記為實驗性,但此上下文的格式可能會變更。

Slots 可以是互動式的,並且包裹在具有 display: contents;<div> 中。

與客戶端元件配對

在這種情況下,.server + .client 元件是元件的兩個「半部」,可以用於在伺服器端和客戶端對元件進行單獨實作的高級用例。

目錄結構
-| components/
---| Comments.client.vue
---| Comments.server.vue
pages/example.vue
<template>
  <div>
    <!-- this component will render Comments.server on the server then Comments.client once mounted in the browser -->
    <Comments />
  </div>
</template>

內建 Nuxt 元件

Nuxt 提供了許多元件,包括 <ClientOnly><DevOnly>。您可以在 API 文件中閱讀更多關於它們的資訊。

文件 > API 中閱讀更多資訊。

函式庫作者

製作具有自動 tree-shaking 和元件註冊的 Vue 元件函式庫非常容易。✨

您可以使用 components:dirs hook 來擴展目錄列表,而無需在您的 Nuxt 模組中進行使用者設定。

想像一下像這樣的目錄結構

目錄結構
-| node_modules/
---| awesome-ui/
-----| components/
-------| Alert.vue
-------| Button.vue
-----| nuxt.js
-| pages/
---| index.vue
-| nuxt.config.js

然後在 awesome-ui/nuxt.js 中,您可以使用 components:dirs hook

import { 
defineNuxtModule
,
createResolver
} from '@nuxt/kit'
export default
defineNuxtModule
({
hooks
: {
'components:dirs': (
dirs
) => {
const {
resolve
} =
createResolver
(import.meta.
url
)
// Add ./components dir to the list
dirs
.
push
({
path
:
resolve
('./components'),
prefix
: 'awesome'
}) } } })

就這樣!現在在您的專案中,您可以在您的 nuxt.config 檔案中將您的 UI 函式庫作為 Nuxt 模組匯入

nuxt.config.ts
export default 
defineNuxtConfig
({
modules
: ['awesome-ui/nuxt']
})

... 並在我們的 pages/index.vue 中直接使用模組元件(以 awesome- 為前綴)。

<template>
  <div>
    My <AwesomeButton>UI button</AwesomeButton>!
    <awesome-alert>Here's an alert!</awesome-alert>
  </div>
</template>

它將僅在使用時自動匯入元件,並且在更新 node_modules/awesome-ui/components/ 中的元件時也支援 HMR。

文件 > 範例 > 功能 > 自動匯入 中閱讀和編輯 live example。