components

components/ 目錄用於存放所有 Vue 元件。

Nuxt 會自動匯入此目錄中的任何元件(以及您可能正在使用的任何模組註冊的元件)。

目錄結構
-| components/
---| AppHeader.vue
---| AppFooter.vue
app/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 屬性。

例如

app/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 處理動態元件,請確保只插入元件名稱,該名稱必須是字面字串,不能是或包含變數。該字串在編譯步驟中進行靜態分析。

或者,儘管不推薦,您可以全域性註冊所有元件,這將為所有元件建立非同步塊,並使它們在整個應用程式中可用。

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

您還可以透過將它們放在 ~/components/global 目錄中,或使用 .global.vue 字尾的檔名,來選擇性地全域性註冊某些元件。如上所述,每個全域性元件都呈現在一個單獨的塊中,因此請注意不要過度使用此功能。

每個元件目錄也可以設定 global 選項。

動態匯入

要動態匯入元件(也稱為元件的延遲載入),您只需在元件名稱前新增 Lazy 字首。如果元件並非總是需要,此功能特別有用。

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

app/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 提供了一系列內建的水合策略。每個惰性元件只能使用一種策略。

惰性水合元件上的任何屬性更改都會立即觸發水合。(例如,更改帶有 hydrate-never 的元件的屬性將導致其水合)
目前,Nuxt 的內建惰性水合僅適用於單檔案元件(SFC),並且要求您在模板中定義屬性(而不是透過 v-bind 展開屬性物件)。它也不適用於從 #components 直接匯入。

hydrate-on-visible

當元件在視口中可見時水合。

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

hydrate-on-idle

當瀏覽器空閒時水合元件。如果需要元件儘快載入但又不阻塞關鍵渲染路徑,則此策略適用。

您還可以傳遞一個數字作為最大超時時間。

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

hydrate-on-interaction

在指定的互動(例如,點選、滑鼠懸停)後水合元件。

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

如果您不傳遞事件或事件列表,它將預設在 pointerenterclickfocus 時水合。

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

hydrate-on-media-query

當視窗匹配媒體查詢時水合元件。

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

hydrate-after

在指定的延遲(以毫秒為單位)後水合元件。

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

hydrate-when

根據布林條件水合元件。

app/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

從不水合元件。

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

監聽水合事件

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

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

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

注意事項和最佳實踐

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

  1. 優先處理視口內內容: 避免對關鍵的、首屏內容進行延遲水合。它最適合不需要立即顯示的內容。
  2. 條件渲染: 在惰性元件上使用 v-if="false" 時,您可能不需要延遲水合。您可以使用普通的惰性元件。
  3. 共享狀態: 請注意多個元件之間的共享狀態 (v-model)。在一個元件中更新模型可能會觸發繫結到該模型的所有元件的水合。
  4. 使用每種策略的預期用例: 每種策略都針對特定目的進行了最佳化。
    • hydrate-when 最適合可能不需要總是水合的元件。
    • hydrate-after 適用於可以等待特定時間的元件。
    • hydrate-on-idle 適用於瀏覽器空閒時可以水合的元件。
  5. 避免在互動式元件上使用 hydrate-never 如果元件需要使用者互動,則不應將其設定為從不水合。

直接匯入

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

app/pages/index.vue
<script setup lang="ts">
import { LazyMountainsList, NuxtLink } 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 包自動匯入元件,您可以在本地模組中使用 addComponent 來註冊它們。

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
app/pages/example.vue
<template>
  <div>
    <!-- this component will only be rendered on client side -->
    <Comments />
  </div>
</template>
此功能僅適用於 Nuxt 自動匯入和 #components 匯入。從其真實路徑顯式匯入這些元件不會將它們轉換為僅客戶端元件。
.client 元件僅在掛載後渲染。要使用 onMounted() 訪問渲染的模板,請在 onMounted() 鉤子的回撥中新增 await nextTick()
您也可以使用 <ClientOnly> 元件實現類似的結果。

伺服器元件

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

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

閱讀 Daniel Roe 的 Nuxt 伺服器元件指南。

獨立伺服器元件

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

當它們的屬性更新時,這將導致網路請求,從而就地更新渲染的 HTML。

伺服器元件目前處於實驗階段,要使用它們,您需要在 nuxt.config 中啟用“元件島”功能

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

現在,您可以使用 .server 字尾註冊僅伺服器元件,並在應用程式中自動隨處使用它們。

目錄結構
-| components/
---| HighlightedMarkdown.server.vue
app/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 屬性和 #fallback 插槽都會傳遞給它。

伺服器元件(和島)必須有一個根元素。(HTML 註釋也視為元素。)
屬性透過 URL 查詢引數傳遞給伺服器元件,因此受限於 URL 的可能長度,因此請注意不要透過屬性向伺服器元件傳遞大量資料。
在其他島內巢狀島時要小心,因為每個島都會增加一些額外的開銷。
僅伺服器元件和島元件的大多數功能(例如插槽和客戶端元件)僅適用於單檔案元件。

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

此功能需要將配置中的 experimental.componentIslands.selectiveClient 設定為 true。

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

app/components/ServerWithClient.vue
<template>
  <div>
    <HighlightedMarkdown markdown="# Headline" />
    <!-- Counter will be loaded and hydrated client-side -->
    <Counter
      nuxt-client
      :count="5"
    />
  </div>
</template>
這僅在伺服器元件中有效。客戶端元件的插槽僅在 experimental.componentIsland.selectiveClient 設定為 'deep' 時才起作用,並且由於它們是在伺服器端渲染的,因此在客戶端不再具有互動性。

伺服器元件上下文

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

這意味著

  • 將在伺服器端建立一個新的 Vue 應用程式來建立 NuxtIslandResponse
  • 渲染元件時將建立一個新的“島上下文”。
  • 您無法從應用程式的其餘部分訪問“島上下文”,也無法從島元件訪問應用程式的其餘部分的上下文。換句話說,伺服器元件或島是與應用程式的其餘部分**隔離**的。
  • 除非您的外掛設定了 env: { islands: false }(您可以在物件語法外掛中這樣做),否則它們將在渲染島時再次執行。

在島元件中,您可以透過 nuxtApp.ssrContext.islandContext 訪問其島上下文。請注意,雖然島元件仍被標記為實驗性,但此上下文的格式可能會更改。

插槽可以互動,並被包裹在帶有 display: contents;<div>

與客戶端元件配對

在這種情況下,.server + .client 元件是一個元件的兩個“一半”,可用於伺服器端和客戶端上元件的單獨實現的高階用例。

目錄結構
-| components/
---| Comments.client.vue
---| Comments.server.vue
app/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 文件中閱讀更多相關資訊。

文件 > 4.X > API 中閱讀更多內容。

庫作者

使用自動搖樹和元件註冊來建立 Vue 元件庫非常簡單。✨

您可以使用 @nuxt/kit 提供的 addComponentsDir 方法在您的 Nuxt 模組中註冊您的元件目錄。

想象一下這樣的目錄結構

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

然後在 awesome-ui/nuxt.ts 中,您可以使用 addComponentsDir 鉤子

import { addComponentsDir, createResolver, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup () {
    const resolver = createResolver(import.meta.url)

    // Add ./components dir to the list
    addComponentsDir({
      path: resolver.resolve('./components'),
      prefix: 'awesome',
    })
  },
})

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

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

...並直接在我們的 app/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。

文件 > 4.X > 示例 > 功能 > 自動匯入 中閱讀並編輯即時示例。