Nuxt Nation 大会即将到来。加入我们,时间为 11 月 12-13 日。

组件

components/ 目录中放置所有 Vue 组件。

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

目录结构
-| components/
---| AppHeader.vue
---| AppFooter.vue
app.vue
<template>
  <div>
    <AppHeader />
    <NuxtPage />
    <AppFooter />
  </div>
</template>

组件名称

如果您的组件位于嵌套目录中,例如

目录结构
-| components/
---| base/
-----| foo/
-------| Button.vue

… 那么组件的名称将基于其自身的路径目录和文件名,重复的段将被删除。因此,组件的名称将是

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

如果要仅根据组件名称(而不是路径)自动导入组件,则需要使用配置对象的扩展形式将 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 属性。

例如

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 处理动态组件,请确保除了组件名称之外不插入任何内容,组件名称必须是字符串而不是变量。
观看 Daniel Roe 关于 resolveComponent 的简短视频。

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

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

您还可以通过将某些组件放在 ~/components/global 目录中或在文件名中使用 .global.vue 后缀来选择性地全局注册一些组件。如上所述,每个全局组件都在单独的块中渲染,因此请注意不要过度使用此功能。

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

动态导入

要动态导入组件(也称为延迟加载组件),您只需在组件名称前添加 Lazy 前缀即可。如果并非始终需要该组件,这将特别有用。

通过使用 Lazy 前缀,您可以延迟加载组件代码直到适当的时刻,这有助于优化 JavaScript 包大小。

pages/index.vue
<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 的自动导入功能,也可以显式地从 #components 导入组件。

pages/index.vue
<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 目录。如果要添加其他目录,或更改如何在该目录的子文件夹中扫描组件,可以将其他目录添加到配置中

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' },
// 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',
}) }, })
任何嵌套目录都需要先添加,因为它们是按顺序扫描的。

组件扩展名

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

nuxt.config.ts
export default 
defineNuxtConfig
({
components
: [
{
path
: '~/components',
extensions
: ['.vue'],
} ] })

客户端组件

如果某个组件仅用于客户端渲染,可以在组件中添加 .client 后缀。

目录结构
| components/
--| Comments.client.vue
pages/example.vue
<template>
  <div>
    <!-- this component will only be rendered on client side -->
    <Comments />
  </div>
</template>
此功能仅适用于 Nuxt 自动导入和 #components 导入。从其真实路径显式导入这些组件不会将它们转换为仅客户端组件。
.client 组件仅在挂载后才渲染。要使用 onMounted() 访问渲染的模板,请在 onMounted() 钩子的回调中添加 await nextTick()
您还可以使用 <ClientOnly> 组件获得类似的结果。

服务器组件

服务器组件允许在客户端应用程序中服务器渲染单个组件。即使您正在生成静态站点,也可以在 Nuxt 中使用服务器组件。这使得构建复杂的站点成为可能,这些站点混合了动态组件、服务器渲染的 HTML 甚至静态标记块。

服务器组件可以单独使用,也可以与 客户端组件 配合使用。

观看 Learn Vue 关于 Nuxt 服务器组件的视频。
阅读 Daniel Roe 关于 Nuxt 服务器组件的指南。

独立服务器组件

独立服务器组件将始终在服务器上渲染,也称为 Island 组件。

当它们的 props 更新时,这将导致网络请求,该请求将就地更新渲染的 HTML。

服务器组件目前处于实验阶段,要使用它们,您需要在 nuxt.config 中启用“组件岛”功能

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

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

目录结构
-| components/
---| HighlightedMarkdown.server.vue
pages/example.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 插槽都传递给它。

服务器组件(以及岛屿组件)必须具有单个根元素。(HTML 注释也被视为元素。)
在其他岛屿组件内嵌套岛屿组件时要小心,因为每个岛屿组件都会增加一些额外的开销。
大多数仅限服务器组件和岛屿组件的功能,例如插槽和客户端组件,仅适用于单文件组件。

服务器组件内的客户端组件

此功能需要在您的配置中将 experimental.componentIslands.selectiveClient 设置为 true。

您可以通过在希望在客户端加载的组件上设置 nuxt-client 属性来部分水化组件。

components/ServerWithClient.vue
<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
pages/example.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 文档中阅读有关它们的更多信息。

文档 > API 中了解更多信息。

库作者

创建具有自动 tree-shaking 和组件注册的 Vue 组件库非常容易。✨

您可以使用 components:dirs 钩子扩展目录列表,而无需在您的 Nuxt 模块中进行用户配置。

想象一下这样的目录结构

目录结构
-| node_modules/
---| awesome-ui/
-----| components/
-------| Alert.vue
-------| Button.vue
-----| nuxt.js
-| pages/
---| index.vue
-| nuxt.config.js

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

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 模块导入

nuxt.config.ts
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。

文档 > 示例 > 功能 > 自动导入 中阅读和编辑实时示例。