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。