components
components/
目錄用於存放所有 Vue 元件。Nuxt 會自動匯入此目錄中的任何元件(以及您可能正在使用的任何模組註冊的元件)。
-| components/
---| AppHeader.vue
---| AppFooter.vue
<template>
<div>
<AppHeader />
<NuxtPage />
<AppFooter />
</div>
</template>
元件命名
如果您在巢狀目錄中有元件,例如
-| components/
---| base/
-----| foo/
-------| Button.vue
...則元件的名稱將基於其自身的路徑目錄和檔名,並移除重複的段。因此,元件的名稱將是
<BaseFooButton />
Button.vue
重新命名為 BaseFooButton.vue
。如果您想僅根據其名稱(而非路徑)自動匯入元件,則需要使用配置物件的擴充套件形式將 pathPrefix
選項設定為 false
export default defineNuxtConfig({
components: [
{
path: '~/components',
pathPrefix: false, },
],
})
這將使用與 Nuxt 2 中相同的策略註冊元件。例如,~/components/Some/MyComponent.vue
將以 <MyComponent>
的形式可用,而不是 <SomeMyComponent>
。
動態元件
如果您想使用 Vue 的 <component :is="someComputedComponent">
語法,您需要使用 Vue 提供的 resolveComponent
輔助函式,或者直接從 #components
匯入元件並將其傳遞給 is
屬性。
例如
<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 包的大小。
<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
的元件的屬性將導致其水合)v-bind
展開屬性物件)。它也不適用於從 #components
直接匯入。hydrate-on-visible
當元件在視口中可見時水合。
<template>
<div>
<LazyMyComponent hydrate-on-visible />
</div>
</template>
hydrateOnVisible
策略.hydrate-on-idle
當瀏覽器空閒時水合元件。如果需要元件儘快載入但又不阻塞關鍵渲染路徑,則此策略適用。
您還可以傳遞一個數字作為最大超時時間。
<template>
<div>
<LazyMyComponent hydrate-on-idle />
</div>
</template>
hydrateOnIdle
策略.hydrate-on-interaction
在指定的互動(例如,點選、滑鼠懸停)後水合元件。
<template>
<div>
<LazyMyComponent hydrate-on-interaction="mouseover" />
</div>
</template>
如果您不傳遞事件或事件列表,它將預設在 pointerenter
、click
和 focus
時水合。
hydrateOnInteraction
策略.hydrate-on-media-query
當視窗匹配媒體查詢時水合元件。
<template>
<div>
<LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
</div>
</template>
hydrateOnMediaQuery
策略.hydrate-after
在指定的延遲(以毫秒為單位)後水合元件。
<template>
<div>
<LazyMyComponent :hydrate-after="2000" />
</div>
</template>
hydrate-when
根據布林條件水合元件。
<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
從不水合元件。
<template>
<div>
<LazyMyComponent hydrate-never />
</div>
</template>
監聽水合事件
所有延遲水合元件在水合時都會發出 @hydrated
事件。
<template>
<div>
<LazyMyComponent
hydrate-on-visible
@hydrated="onHydrate"
/>
</div>
</template>
<script setup lang="ts">
function onHydrate () {
console.log('Component has been hydrated!')
}
</script>
注意事項和最佳實踐
延遲水合可以提供效能優勢,但正確使用它至關重要
- 優先處理視口內內容: 避免對關鍵的、首屏內容進行延遲水合。它最適合不需要立即顯示的內容。
- 條件渲染: 在惰性元件上使用
v-if="false"
時,您可能不需要延遲水合。您可以使用普通的惰性元件。 - 共享狀態: 請注意多個元件之間的共享狀態 (
v-model
)。在一個元件中更新模型可能會觸發繫結到該模型的所有元件的水合。 - 使用每種策略的預期用例: 每種策略都針對特定目的進行了最佳化。
hydrate-when
最適合可能不需要總是水合的元件。hydrate-after
適用於可以等待特定時間的元件。hydrate-on-idle
適用於瀏覽器空閒時可以水合的元件。
- 避免在互動式元件上使用
hydrate-never
: 如果元件需要使用者互動,則不應將其設定為從不水合。
直接匯入
如果您想或需要繞過 Nuxt 的自動匯入功能,您也可以從 #components
顯式匯入元件。
<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
目錄。如果您想新增其他目錄,或更改在此目錄的子資料夾中掃描元件的方式,您可以向配置新增其他目錄
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',
})
},
})
<template>
<div>
<!-- the component uses the name we specified and is auto-imported -->
<MyAutoImportedComponent />
</div>
</template>
元件副檔名
預設情況下,任何具有 nuxt.config.ts
的 extensions 鍵中指定的副檔名的檔案都被視為元件。如果您需要限制應註冊為元件的副檔名,您可以使用元件目錄宣告的擴充套件形式及其 extensions
鍵
export default defineNuxtConfig({
components: [
{
path: '~/components',
extensions: ['.vue'], },
],
})
客戶端元件
如果元件只用於客戶端渲染,您可以將 .client
字尾新增到您的元件。
| components/
--| Comments.client.vue
<template>
<div>
<!-- this component will only be rendered on client side -->
<Comments />
</div>
</template>
#components
匯入。從其真實路徑顯式匯入這些元件不會將它們轉換為僅客戶端元件。.client
元件僅在掛載後渲染。要使用 onMounted()
訪問渲染的模板,請在 onMounted()
鉤子的回撥中新增 await nextTick()
。伺服器元件
伺服器元件允許在客戶端應用程式中伺服器渲染單個元件。即使您正在生成靜態站點,也可以在 Nuxt 中使用伺服器元件。這使得構建混合了動態元件、伺服器渲染 HTML 甚至靜態標記塊的複雜站點成為可能。
伺服器元件可以單獨使用,也可以與客戶端元件配對使用。
獨立伺服器元件
獨立伺服器元件將始終在伺服器上渲染,也稱為 Islands 元件。
當它們的屬性更新時,這將導致網路請求,從而就地更新渲染的 HTML。
伺服器元件目前處於實驗階段,要使用它們,您需要在 nuxt.config 中啟用“元件島”功能
export default defineNuxtConfig({
experimental: {
componentIslands: true,
},
})
現在,您可以使用 .server
字尾註冊僅伺服器元件,並在應用程式中自動隨處使用它們。
-| components/
---| HighlightedMarkdown.server.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
插槽都會傳遞給它。
伺服器元件中的客戶端元件
experimental.componentIslands.selectiveClient
設定為 true。您可以透過在希望在客戶端載入的元件上設定 nuxt-client
屬性來部分水合元件。
<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
<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 文件中閱讀更多相關資訊。
庫作者
使用自動搖樹和元件註冊來建立 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 模組匯入
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。