components
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
prop 中。
例如
<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 提供了一系列內建的水合策略。每個惰性組件只能使用一種策略。
v-bind
展開 prop 物件)。它也不適用於從 #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
和 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="onHydrated" />
</div>
</template>
<script setup lang="ts">
function onHydrate() {
console.log("Component has been hydrated!")
}
</script>
注意事項與最佳實踐
延遲水合可以提供效能優勢,但正確使用它至關重要
- 優先處理視口內內容: 避免對關鍵的、首屏內容使用延遲水合。它最適合用於非立即需要的內容。
- 條件渲染: 當在惰性組件上使用
v-if="false"
時,您可能不需要延遲水合。您可以直接使用一般的惰性組件。 - 共享狀態: 請留意跨多個元件的共享狀態 (
v-model
)。在一個元件中更新 model 可能會觸發所有綁定到該 model 的元件進行 hydration。 - 善用各策略的預期用途: 每個策略都針對特定目的進行了最佳化。
hydrate-when
最適合可能不一定需要 hydration 的元件。hydrate-after
適用於可以等待特定時間的元件。hydrate-on-idle
適用於可以在瀏覽器閒置時進行 hydration 的元件。
- 避免在互動式元件上使用
hydrate-never
:如果元件需要使用者互動,則不應設定為永不 hydration。
直接匯入
如果您想要或需要繞過 Nuxt 的自動匯入功能,您也可以從 #components
明確地匯入元件。
<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
目錄。如果您想要新增其他目錄,或變更如何掃描此目錄子資料夾中的元件,您可以將其他目錄新增至設定檔。
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
在 local 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
鍵。
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()
hook 的回呼中新增 await nextTick()
。伺服器元件
伺服器元件允許在您的客戶端應用程式中伺服器渲染個別元件。即使您正在產生靜態網站,也可以在 Nuxt 中使用伺服器元件。這使得構建複雜網站成為可能,這些網站混合了動態元件、伺服器渲染的 HTML 甚至靜態標記區塊。
伺服器元件可以單獨使用,也可以與 客戶端元件 配對使用。
獨立伺服器元件
獨立伺服器元件將始終在伺服器上渲染,也稱為 Islands 元件。
當它們的 props 更新時,這將導致一個網路請求,該請求將就地更新渲染的 HTML。
伺服器元件目前處於實驗階段,為了使用它們,您需要在您的 nuxt.config 中啟用 'component islands' 功能。
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
prop 和 #fallback
slot 都會傳遞給它。
伺服器元件內的客戶端元件
experimental.componentIslands.selectiveClient
為 true。您可以通過在您希望在客戶端載入的元件上設定 nuxt-client
屬性來部分 hydration 元件。
<template>
<div>
<HighlightedMarkdown markdown="# Headline" />
<!-- Counter will be loaded and hydrated client-side -->
<Counter nuxt-client :count="5" />
</div>
</template>
experimental.componentIsland.selectiveClient
設定為 'deep'
時才有效,並且由於它們是在伺服器端渲染的,因此一旦在客戶端就無法互動。伺服器元件上下文
當渲染僅限伺服器或 island 元件時,<NuxtIsland>
會發出一個 fetch 請求,該請求會返回一個 NuxtIslandResponse
。(如果伺服器端渲染,這是一個內部請求;如果是客戶端導航渲染,您可以在網路選項卡中看到該請求。)
這表示
- 將在伺服器端建立一個新的 Vue 應用程式,以建立
NuxtIslandResponse
。 - 在渲染元件時,將建立一個新的「island 上下文」。
- 您無法從應用程式的其餘部分存取「island 上下文」,也無法從 island 元件存取應用程式其餘部分的上下文。換句話說,伺服器元件或 island 與應用程式的其餘部分是隔離的。
- 您的插件將在渲染 island 時再次運行,除非它們設定了
env: { islands: false }
(您可以在物件語法插件中執行此操作)。
在 island 元件中,您可以通過 nuxtApp.ssrContext.islandContext
存取其 island 上下文。請注意,雖然 island 元件仍標記為實驗性,但此上下文的格式可能會變更。
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 文件中閱讀更多關於它們的資訊。
函式庫作者
製作具有自動 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 模組匯入
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。