通过 100+ 个技巧学习 Nuxt!

components

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 prop 中。

例如

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.tsextensions 键中指定了扩展名的文件都被视为组件。 如果你需要限制应注册为组件的文件扩展名,你可以使用组件目录声明的扩展形式及其 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 服务端组件的指南。

独立的服务端组件

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

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

服务端组件目前是实验性的,为了使用它们,你需要在你的 nuxt.config 中启用 'component islands' 功能。

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 prop 和 #fallback 插槽都会传递给它。

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

服务端组件中的客户端组件

此功能需要在你的配置中将 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' 时才起作用,并且由于它们是在服务器端渲染的,因此一旦客户端渲染就不是交互式的。

服务端组件上下文

当渲染仅限服务器或 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
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。

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