组件目录 Components

components/ 目录是放置所有 Vue 组件的地方。

components/ 目录是您放置所有 Vue 组件的地方,这些组件随后可以导入到您的页面或其他组件中(了解更多)。

Nuxt 会自动导入 components/ 目录中的所有组件(以及您可能正在使用的任何模块注册的组件)。

bash
| components/
--| TheHeader.vue
--| TheFooter.vue
layouts/default.vue
html
<template>
  <div>
    <TheHeader />
    <slot />
    <TheFooter />
  </div>
</template>

自定义目录

默认情况下,只扫描 ~/components 目录。 如果要添加其他目录,或更改组件在此目录的子文件夹中的扫描方式,您可以将其他目录添加到配置中:

nuxt.config.ts
ts
export default defineNuxtConfig({
  components: [
    { path: '~/components/special-components', prefix: 'Special' },
    '~/components'
  ]
})

任何嵌套目录都需要先添加,因为它们是按顺序扫描的。

组件扩展

默认情况下,在 nuxt.config.ts 的扩展键 中指定扩展名的任何文件都被视为组件。 如果需要限制应注册为组件的文件扩展名,可以使用组件目录声明的扩展形式及其 extensions 键:

diff
export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
+     extensions: ['.vue'],
    }
  ]
})

组件名称

如果您在嵌套目录中有一个组件,例如:

bash
| components/
--| base/
----| foo/
------| Button.vue

...然后组件的名称将基于其自己的路径目录和文件名,并删除重复的段。 因此,组件的名称将是:

html
<BaseFooButton />

为清楚起见,我们建议组件的文件名与其名称相匹配。 (因此,在上面的示例中,您可以将 Button.vue 重命名为 BaseFooButton.vue。)

如果你想只根据名称而不是路径自动导入组件,那么你需要使用配置对象的扩展形式将 pathPrefix 选项设置为 false

diff
export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
+     pathPrefix: false,
    },
  ],
});

这使用与 Nuxt 2 中使用的相同策略注册组件。例如,~/components/Some/MyComponent.vue 将用作<MyComponent> 而不是<SomeMyComponent>

动态组件

如果你想使用<component :is="someComputedComponent"> 这样的Vue语法,那么你需要使用 Vue 提供的 resolveComponent 助手。

vue
<script setup>
const MyButton = resolveComponent('MyButton')
</script>

<template>
  <component :is="clickable ? MyButton : 'div'" />
</template>

如果您使用resolveComponent来处理动态组件,请确保除了组件名称之外不要插入任何内容,该名称必须是字符串而不是变量。

或者,虽然不推荐,但您可以全局注册所有组件,这将为所有组件创建异步块,并使它们在整个应用程序中可用。

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

您还可以通过将它们放在 ~/components/global 目录中来选择性地全局注册一些组件。

也可以为每个组件目录设置 global 选项。

动态导入

要动态导入组件(也称为延迟加载组件),您需要做的就是在组件名称中添加Lazy前缀。

layouts/default.vue
html
<template>
  <div>
    <TheHeader />
    <slot />
    <LazyTheFooter />
  </div>
</template>

如果并不总是需要该组件,这将特别有用。 通过使用 Lazy 前缀,您可以延迟加载组件代码直到合适的时刻,这有助于优化您的 JavaScript 包大小。

pages/index.vue
html
<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      show: false
    }
  }
}
</script>

显式导入

如果你想或需要绕过 Nuxt 的自动导入功能,你也可以从 #components 显式导入组件。

pages/index.vue
html
<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>

<script setup>
  import { NuxtLink, LazyMountainsList } from '#components'
  const show = ref(false)
</script>

客户端渲染组件

Nuxt 提供了 <ClientOnly> 组件,目的是只在客户端渲染一个组件。 要仅在客户端导入组件,请在仅客户端插件中注册该组件。

pages/example.vue
html
<template>
  <div>
    <Sidebar />
    <ClientOnly>
      <!-- 该组件只会在客户端呈现 -->
      <Comments />
    </ClientOnly>
  </div>
</template>

使用插槽作为后备,直到将<ClientOnly>安装在客户端。

pages/example.vue
html
<template>
  <div>
    <Sidebar />
    <!-- 这会在服务器端呈现“span”元素 -->
    <ClientOnly fallbackTag="span">
      <!-- 该组件只会在客户端呈现 -->
      <Comments />
      <template #fallback>
        <!-- 这将在服务器端呈现 -->
        <p>Loading comments...</p>
      </template>
    </ClientOnly>
  </div>
</template>

客户端专用组件

如果一个组件只打算在客户端呈现,您可以将 .client 后缀添加到您的组件。

bash
| components/
--| Comments.client.vue
pages/example.vue
html
<template>
  <div>
    <!-- 该组件只会在客户端呈现 -->
    <Comments />
  </div>
</template>

此功能仅适用于 Nuxt 自动导入和 #components 导入。 从它们的真实路径显式导入这些组件不会将它们转换为仅客户端组件。

.client 组件只有在挂载后才会呈现。
要使用 onMounted() 访问呈现的模板,请在 onMounted() 挂钩的回调中添加 await nextTick()

服务端专用组件

.server 组件可以单独使用,也可以与 .client 组件搭配使用。

独立服务端组件

独立服务端组件将始终在服务端上呈现。 当他们的道具更新时,这将导致网络请求将就地更新呈现的 HTML。

服务器组件目前处于试验阶段,为了使用它们,您需要在 nuxt.config 中启用component islands功能:

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

现在,您可以使用 .server 后缀注册仅限服务器的组件,并自动在应用程序的任何位置使用它们。

bash
| components/
--| HighlightedMarkdown.server.vue
pages/example.vue
html
<template>
  <div>
    <!-- 这将自动在服务器上呈现,这意味着您的Markdown解析 + 高亮显示库不包含在您的客户端包中。 -->
    <HighlightedMarkdown markdown="# Headline" />
  </div>
</template>

服务器组件在其当前开发状态下不支持插槽。

.client组件配对

在这种情况下,.server + .client 组件是一个组件的“两半”,可以在高级用例中用于在服务器端和客户端分别实现组件。

bash
| components/
--| Comments.client.vue
--| Comments.server.vue
pages/example.vue
html
<template>
  <div>
    <!-- 该组件将呈现 Comments.server 服务器端,然后在客户端安装 Comments.client -->
    <Comments />
  </div>
</template>

在组件的客户端部分中,hydration是必要的,以使其能够在服务器端渲染的HTML基础上进行“水合”(即将组件的客户端部分重新激活并绑定事件等操作)。也就是说,组件在初始加载时应该呈现相同的HTML内容,否则会出现“hydration mismatch”(水合不匹配)的情况。

在这里,hydrate指的是将已有的HTML内容重新激活并绑定事件等操作,使其成为可交互的客户端组件的过程。目前有几种中文称呼:水合/补水,还有叫滋润的,哈哈。

开发时专用组件

Nuxt 提供了 <DevOnly> 组件来仅在开发期间渲染组件。

内容不会包含在生产构建和 tree-shaking 中。

pages/example.vue
html
<template>
  <div>
    <Sidebar />
    <DevOnly>
      <!-- 该组件只会在开发期间呈现 -->
      <LazyDebugBar />
    </DevOnly>
  </div>
</template>

客户端回调组件

Nuxt 提供了 <NuxtClientFallback> 组件,用于在 SSR 出现错误时在客户端上渲染其内容。你可以指定 fallbackTag 属性,以便在无法在服务器上渲染时渲染特定的标签。

pages/example.vue
html
<template>
  <div>
    <Sidebar />
    <!-- 该组件将在客户端呈现 -->
    <NuxtClientFallback fallback-tag="span">
      <Comments />
      <BrokeInSSR />
    </NuxtClientFallback>
  </div>
</template>

库作者

对于库开发者来说,使用 Nuxt 创建自动进行Tree-Shaking和组件注册的 Vue 组件库非常容易。你可以使用components:dirs钩子扩展目录列表,而无需在你的 Nuxt 模块中要求用户进行配置。

假设你有以下目录结构:

bash
| node_modules/
---| awesome-ui/
------| components/
---------| Alert.vue
---------| Button.vue
------| nuxt.js
| pages/
---| index.vue
| nuxt.config.js

然后在 awesome-ui/nuxt.js 中你可以使用 components:dirs 钩子:

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

export default defineNuxtModule({
  hooks: {
    'components:dirs': (dirs) => {
      const { resolve } = createResolver(import.meta.url)
      // 将 ./components 目录添加到列表中
      dirs.push({
        path: fileURLToPath(resolve('./components')),
        prefix: 'awesome'
      })
    }
  }
})

就是这样! 现在在您的项目中,您可以将 UI 库作为 Nuxt 模块导入到您的 nuxt.config 文件中:

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

...并直接在我们的 pages/index.vue 中使用模块组件(以 awesome- 为前缀):

vue
<template>
  <div>
    My <AwesomeButton>UI button</AwesomeButton>!
    <awesome-alert>Here's an alert!</awesome-alert>
  </div>
</template>

它会仅在使用时自动导入组件,并且在更新 node_modules/awesome-ui/components/ 中的组件时也支持 HMR。

案例演示: