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

如果你想在不影响组件名称的情况下,将组件分组到一个目录中,可以使用圆括号 ( ) 来命名分组目录。

Grouping Directory
-| components/
---| base/
-----| (foo)/
-------| Button.vue

……那么组件名称将跳过分组目录,结果为:

<BaseButton />

如果你只想根据组件名称而不是路径自动导入组件,那么需要使用扩展形式的配置对象,将 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 后缀,来有选择地将某些组件注册为全局组件。如上所述,每个全局组件都会被渲染为单独的 chunk,所以请谨慎使用此功能。

global 选项也可以针对每个组件目录单独设置。

动态导入

要动态导入组件(也称为懒加载组件),只需在组件名称前添加 Lazy 前缀。当组件并非始终需要时,这种方式尤其有用。

使用 Lazy 前缀,可以将组件代码的加载延迟到适当的时间,从而帮助优化 JavaScript 包的大小。

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

<template>
  <div>
    <h1>山脉</h1>
    <LazyMountainsList v-if="show" />
    <button
      v-if="!show"
      @click="show = true"
    >
      显示列表
    </button>
  </div>
</template>

延迟(或懒)水合

懒加载组件有助于控制应用中的 chunk 大小,但它们并不总是能提升运行时性能,因为除非条件渲染,它们仍会被主动加载。在真实应用中,一些页面可能包含大量内容和组件,而大多数情况下并非所有组件在页面加载时都需要交互。让它们全部主动加载可能会对性能产生负面影响。

为了优化应用,你可能希望延迟某些组件的水合,直到它们可见,或直到浏览器完成更重要的任务。

Nuxt 支持使用懒(或延迟)水合,让你可以控制组件何时变为可交互状态。

水合策略

Nuxt 提供了一系列内置的水合策略。每个懒组件只能使用一种策略。

对懒加载水合组件的任何 prop 更改都会立即触发水合。(例如,对具有 hydrate-never 的组件更改 prop 将导致其水合)
目前 Nuxt 的内置懒加载水合仅在单文件组件(SFC)中工作,并且要求你在模板中定义 prop(而不是通过 v-bind 展开一个 props 对象)。它也不适用于从 #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 () {
  // 触发自定义水合策略...
  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('组件已经完成水合!')
}
</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>山脉</h1>
    <LazyMountainsList v-if="show" />
    <button
      v-if="!show"
      @click="show = true"
    >
      显示列表
    </button>
    <NuxtLink to="/">首页</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' },

    // 如果你希望覆盖子目录中组件,确保此项放在最后。
    //
    // ~/components/Btn.vue => <Btn />
    // ~/components/base/Btn.vue => <BaseBtn />
    '~/components',
  ],
})
任何嵌套目录需要优先添加,因为它们是按顺序扫描的。

每个目录条目也接受 patternignore glob 选项,它们用于控制在 path 中扫描哪些文件。当你的组件位于不遵循默认布局的嵌套文件夹中时,这很有用,例如一种领域驱动的结构:

nuxt.config.ts
export default defineNuxtConfig({
  components: [
    // ~/domains/blog/components/PostCard.vue => <PostCard />
    {
      path: '~/domains',
      pattern: '*/components/**',
      pathPrefix: false,
    },
  ],
})
如果指定了 patternextensions 选项将不起作用,因此请确保你的 pattern 能匹配你想要扫描的文件扩展名。

npm 包

如果你想从 npm 包自动导入组件,可以在本地模块中使用 addComponent 来注册它们。

import { addComponent, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup () {
    // 从 'my-npm-package' 导入 { MyComponent as MyAutoImportedComponent }
    addComponent({
      name: 'MyAutoImportedComponent',
      export: 'MyComponent',
      filePath: 'my-npm-package',
    })
  },
})
你也可以使用 <ClientOnly> 组件实现类似的效果。 ::♀♀♀♀♀♀

服务端组件

服务端组件允许在客户端应用中对单个组件进行服务器渲染。即使你生成的是静态站点,也可以在 Nuxt 中使用服务端组件。这使得构建混合了动态组件、服务器渲染 HTML 甚至静态标记块的复杂站点成为可能。服务端组件可以单独使用,也可以与 客户端组件 配对使用。
阅读 Daniel Roe 关于 Nuxt 服务端组件的指南。
在专门的服务端组件指南中详细了解 islands 的渲染方式、隔离的 island 上下文、使用 nuxt-client 的选择性水合、插槽、缓存以及当前的限制。
Docs > 4 X > API 中查看详情
Read and edit a live example in Docs > 4 X > Examples > Features > Auto Imports.