components
Nuxt 会自动导入此目录中的所有组件(以及你可能正在使用的任何模块注册的组件)。
¥Nuxt automatically imports any components in this directory (along with components that are registered by any modules you may be using).
-| components/
---| AppHeader.vue
---| AppFooter.vue
<template>
<div>
<AppHeader />
<NuxtPage />
<AppFooter />
</div>
</template>
组件名称
¥Component Names
如果你的组件位于嵌套目录中,例如:
¥If you have a component in nested directories such as:
-| components/
---| base/
-----| foo/
-------| Button.vue
...则组件的名称将基于其自身的路径目录和文件名,并删除重复的片段。因此,组件的名称将为:
¥... then the component's name will be based on its own path directory and filename, with duplicate segments being removed. Therefore, the component's name will be:
<BaseFooButton />
Button.vue
重命名为 BaseFooButton.vue
。如果你想仅根据组件名称(而非路径)自动导入组件,则需要使用配置对象的扩展形式将 pathPrefix
选项设置为 false
:
¥If you want to auto-import components based only on its name, not path, then you need to set pathPrefix
option to false
using extended form of the configuration object:
export default defineNuxtConfig({
components: [
{
path: '~/components',
pathPrefix: false, },
],
});
此方法使用与 Nuxt 2 中相同的策略注册组件。例如,~/components/Some/MyComponent.vue
可以用作 <MyComponent>
,而不能用作 <SomeMyComponent>
。
¥This registers the components using the same strategy as used in Nuxt 2. For example, ~/components/Some/MyComponent.vue
will be usable as <MyComponent>
and not <SomeMyComponent>
.
动态组件
¥Dynamic Components
如果你想使用 Vue <component :is="someComputedComponent">
语法,则需要使用 Vue 提供的 resolveComponent
辅助函数,或者直接从 #components
导入组件并将其传递给 is
属性。
¥If you want to use the Vue <component :is="someComputedComponent">
syntax, you need to use the resolveComponent
helper provided by Vue or import the component directly from #components
and pass it into is
prop.
例如:
¥For example:
<script setup lang="ts">
import { SomeComponent } from '#components'
const MyButton = resolveComponent('MyButton')
</script>
<template>
<component :is="clickable ? MyButton : 'div'" />
<component :is="SomeComponent" />
</template>
resolveComponent
处理动态组件,请确保除了组件名称之外不要插入任何其他内容。组件名称必须是文字字符串,并且不能是或包含变量。该字符串在编译阶段会被静态分析。或者,虽然不推荐,但你可以全局注册所有组件,这将为所有组件创建异步块,并使其在整个应用中可用。
¥Alternatively, though not recommended, you can register all your components globally, which will create async chunks for all your components and make them available throughout your application.
export default defineNuxtConfig({
components: {
+ global: true,
+ dirs: ['~/components']
},
})
你还可以通过将某些组件放在 ~/components/global
目录中或在文件名中使用 .global.vue
后缀来选择性地全局注册它们。如上所述,每个全局组件都在单独的块中渲染,因此请注意不要过度使用此功能。
¥You can also selectively register some components globally by placing them in a ~/components/global
directory, or by using a .global.vue
suffix in the filename. As noted above, each global component is rendered in a separate chunk, so be careful not to overuse this feature.
global
选项也可以按组件目录设置。动态导入
¥Dynamic Imports
要动态导入组件(也称为延迟加载组件),只需在组件名称中添加 Lazy
前缀即可。如果组件并非总是需要,则尤其有用。
¥To dynamically import a component (also known as lazy-loading a component) all you need to do is add the Lazy
prefix to the component's name. This is particularly useful if the component is not always needed.
通过使用 Lazy
前缀,你可以将组件代码的加载延迟到合适的时机,这有助于优化 JavaScript 包的大小。
¥By using the Lazy
prefix you can delay loading the component code until the right moment, which can be helpful for optimizing your JavaScript bundle size.
<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>
延迟加载(或惰性加载)
¥Delayed (or Lazy) Hydration
惰性加载组件非常适合控制应用中的块大小,但它们并不总是能提升运行时性能,因为它们仍然会主动加载,除非有条件地进行渲染。在实际应用中,某些页面可能包含大量内容和组件,并且大多数情况下,并非所有页面都需要在页面加载后立即进行交互。让所有模块都急切加载会对性能产生负面影响。
¥Lazy components are great for controlling the chunk sizes in your app, but they don't always enhance runtime performance, as they still load eagerly unless conditionally rendered. In real-world applications, some pages may include a lot of content and a lot of components, and most of the time not all of them need to be interactive as soon as the page is loaded. Having them all load eagerly can negatively impact performance.
为了优化你的应用,你可能需要延迟某些组件的 hydration,直到它们可见,或者直到浏览器完成更重要的任务。
¥In order to optimize your app, you may want to delay the hydration of some components until they're visible, or until the browser is done with more important tasks.
Nuxt 使用延迟加载 (Lazy Hydration) 来实现此功能,允许你控制组件何时进行交互。
¥Nuxt supports this using lazy (or delayed) hydration, allowing you to control when components become interactive.
混合策略
¥Hydration Strategies
Nuxt 提供了一系列内置的 hydration 策略。每个惰性加载组件只能使用一种策略。
¥Nuxt provides a range of built-in hydration strategies. Only one strategy can be used per lazy component.
v-bind
扩展 props 对象)。它也不适用于从 #components
直接导入。¥Currently Nuxt's built-in lazy hydration only works in single-file components (SFCs), and requires you to define the prop in the template (rather than spreading an object of props via v-bind
). It also does not work with direct imports from #components
.hydrate-on-visible
当组件在视口中可见时,对其进行水合操作。
¥Hydrates the component when it becomes visible in the viewport.
<template>
<div>
<LazyMyComponent hydrate-on-visible />
</div>
</template>
:
了解更多关于 hydrate-on-visible
选项的信息。
¥Read more about the options for hydrate-on-visible
.
::
hydrateOnVisible
strategy。hydrate-on-idle
当浏览器空闲时,对其进行水合操作。如果你需要组件尽快加载,但又不阻塞关键渲染路径,则此方法非常适用。
¥Hydrates the component when the browser is idle. This is suitable if you need the component to load as soon as possible, but not block the critical rendering path.
你还可以传递一个数字作为最大超时时间。
¥You can also pass a number which serves as a max timeout.
<template>
<div>
<LazyMyComponent hydrate-on-idle />
</div>
</template>
hydrateOnIdle
strategy。hydrate-on-interaction
在指定的交互(例如,点击、鼠标悬停)后对组件进行混合。
¥Hydrates the component after a specified interaction (e.g., click, mouseover).
<template>
<div>
<LazyMyComponent hydrate-on-interaction="mouseover" />
</div>
</template>
如果你未传递事件或事件列表,则默认在 pointerenter
和 focus
上进行 hydrate 操作。
¥If you do not pass an event or list of events, it defaults to hydrating on pointerenter
and focus
.
hydrateOnInteraction
strategy。hydrate-on-media-query
当窗口匹配媒体查询时,对其进行水合操作。
¥Hydrates the component when the window matches a media query.
<template>
<div>
<LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
</div>
</template>
hydrateOnMediaQuery
strategy。hydrate-after
在指定的延迟(以毫秒为单位)后对组件进行混合。
¥Hydrates the component after a specified delay (in milliseconds).
<template>
<div>
<LazyMyComponent :hydrate-after="2000" />
</div>
</template>
hydrate-when
根据布尔条件对组件进行混合。
¥Hydrates the component based on a boolean condition.
<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
永不水合组件。
¥Never hydrates the component.
<template>
<div>
<LazyMyComponent hydrate-never />
</div>
</template>
监听 Hydration 事件
¥Listening to Hydration Events
所有延迟加载组件在加载时都会发出 @hydrated
事件。
¥All delayed hydration components emit a @hydrated
event when they are hydrated.
<template>
<div>
<LazyMyComponent hydrate-on-visible @hydrated="onHydrate" />
</div>
</template>
<script setup lang="ts">
function onHydrate() {
console.log("Component has been hydrated!")
}
</script>
注意事项和最佳实践
¥Caveats and Best Practices
延迟加载可以带来性能优势,但正确使用它至关重要:
¥Delayed hydration can offer performance benefits, but it's essential to use it correctly:
- 优先显示视口内内容:避免关键首屏内容的延迟加载。它最适合用于非立即需要的内容。
- 条件渲染:在惰性加载组件上使用
v-if="false"
时,你可能不需要延迟加载。你可以直接使用普通的惰性加载组件。 - 共享状态:请注意跨多个组件的共享状态 (
v-model
)。更新一个组件中的模型可以触发绑定到该模型的所有组件的 hydration。 - 使用每个策略的预期用例:每种策略都针对特定目的进行了优化。
hydrate-when
最适合那些可能并不总是需要“hydrated”的组件。hydrate-after
适用于可等待特定时间的组件。hydrate-on-idle
适用于可在浏览器空闲时“hydrated”的组件。
- 避免在交互式组件上使用
hydrate-never
:如果组件需要用户交互,则不应将其设置为“永不混合”。
直接导入
¥Direct Imports
如果你想要或需要绕过 Nuxt 的自动导入功能,你还可以从 #components
显式导入组件。
¥You can also explicitly import components from #components
if you want or need to bypass Nuxt's auto-importing functionality.
<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>
自定义目录
¥Custom Directories
默认情况下,仅扫描 ~/components
目录。如果你想添加其他目录,或更改此目录子文件夹中组件的扫描方式,你可以在配置中添加其他目录:
¥By default, only the ~/components
directory is scanned. If you want to add other directories, or change how the components are scanned within a subfolder of this directory, you can add additional directories to the configuration:
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 Packages
如果你想从 npm 包自动导入组件,你可以在 本地模块 中使用 addComponent
来注册它们。
¥If you want to auto-import components from an npm package, you can use addComponent
in a local module to register them.
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>
组件扩展
¥Component Extensions
默认情况下,任何具有 nuxt.config.ts
的扩展键 中指定扩展名的文件都被视为组件。如果你需要限制应注册为组件的文件扩展名,可以使用组件目录声明的扩展形式及其 extensions
键:
¥By default, any file with an extension specified in the extensions key of nuxt.config.ts
is treated as a component.
If you need to restrict the file extensions that should be registered as components, you can use the extended form of the components directory declaration and its extensions
key:
export default defineNuxtConfig({
components: [
{
path: '~/components',
extensions: ['.vue'], }
]
})
客户端组件
¥Client Components
如果组件仅在客户端渲染,则可以为组件添加 .client
后缀。
¥If a component is meant to be rendered only client-side, you can add the .client
suffix to your component.
| components/
--| Comments.client.vue
<template>
<div>
<!-- this component will only be rendered on client side -->
<Comments />
</div>
</template>
#components
导入。从实际路径显式导入这些组件并不会将它们转换为仅供客户端使用的组件。.client
组件仅在挂载后才会渲染。要使用 onMounted()
访问渲染后的模板,请在 onMounted()
钩子的回调中添加 await nextTick()
。:
你也可以使用 <ClientOnly>
组件实现类似的结果。
¥You can also achieve a similar result with the <ClientOnly>
component.
::
服务器组件
¥Server Components
服务器组件允许在客户端应用中使用服务器端渲染的单个组件。即使你正在生成静态站点,也可以在 Nuxt 中使用服务器组件。这使得构建混合动态组件、服务器渲染 HTML 甚至静态标记块的复杂网站成为可能。
¥Server components allow server-rendering individual components within your client-side apps. It's possible to use server components within Nuxt, even if you are generating a static site. That makes it possible to build complex sites that mix dynamic components, server-rendered HTML and even static chunks of markup.
服务器组件可以单独使用,也可以与 客户端组件 搭配使用。
¥Server components can either be used on their own or paired with a client component.
独立服务器组件
¥Standalone server components
独立服务器组件(也称为 Islands 组件)将始终在服务器上渲染。
¥Standalone server components will always be rendered on the server, also known as Islands components.
当它们的 props 更新时,这将导致网络请求,该请求将就地更新已渲染的 HTML。
¥When their props update, this will result in a network request that will update the rendered HTML in-place.
服务器组件目前处于实验阶段,要使用它们,你需要在 nuxt.config 中启用 '组件岛' 功能:
¥Server components are currently experimental and in order to use them, you need to enable the 'component islands' feature in your nuxt.config:
export default defineNuxtConfig({
experimental: {
componentIslands: true
}
})
现在,你可以使用 .server
后缀注册仅服务端组件,并在应用的任何位置自动使用它们。
¥Now you can register server-only components with the .server
suffix and use them anywhere in your application automatically.
-| 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
属性都会传递给它。
¥Server-only components use <NuxtIsland>
under the hood, meaning that lazy
prop and #fallback
slot are both passed down to it.
服务器组件中的客户端组件
¥Client components within server components
experimental.componentIslands.selectiveClient
才能启用。你可以通过在你希望在客户端加载的组件上设置 nuxt-client
属性来部分地补充组件。
¥You can partially hydrate a component by setting a nuxt-client
attribute on the component you wish to be loaded client-side.
<template>
<div>
<HighlightedMarkdown markdown="# Headline" />
<!-- Counter will be loaded and hydrated client-side -->
<Counter nuxt-client :count="5" />
</div>
</template>
experimental.componentIsland.selectiveClient
设置为 'deep'
时才有效,并且由于它们是在服务器端渲染的,因此一旦进入客户端就无法交互。服务器组件上下文
¥Server Component Context
渲染服务器端或孤岛组件时,<NuxtIsland>
会发出一个 fetch 请求,并返回一个 NuxtIslandResponse
。(如果在服务器上渲染,则为内部请求;如果在客户端导航上渲染,则为你可以在网络选项卡中看到的请求。)
¥When rendering a server-only or island component, <NuxtIsland>
makes a fetch request which comes back with a NuxtIslandResponse
. (This is an internal request if rendered on the server, or a request that you can see in the network tab if it's rendering on client-side navigation.)
这意味着:
¥This means:
- 将在服务器端创建一个新的 Vue 应用来创建
NuxtIslandResponse
。 - 将在渲染组件时创建一个新的 '独立上下文'。
- 你无法从应用的其余部分访问 '独立上下文',也无法从 island 组件访问应用其余部分的上下文。换句话说,服务器组件或孤岛与应用的其余部分隔离。
- 你的插件将在渲染岛屿时再次运行,除非它们已设置
env: { islands: false }
(你可以在对象语法插件中设置)。
在 island 组件中,你可以通过 nuxtApp.ssrContext.islandContext
访问其 island 上下文。请注意,虽然孤岛组件仍标记为实验性,但此上下文的格式可能会发生变化。
¥Within an island component, you can access its island context through nuxtApp.ssrContext.islandContext
. Note that while island components are still marked as experimental, the format of this context may change.
display: contents;
的 <div>
中。与客户端组件配对
¥Paired with a Client component
在这种情况下,.server
+ .client
组件是一个组件的两个 'halves',可以在高级用例中用于在服务器端和客户端分别实现组件。
¥In this case, the .server
+ .client
components are two 'halves' of a component and can be used in advanced use cases for separate implementations of a component on server and client side.
-| 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 组件
¥Built-In Nuxt Components
Nuxt 提供了许多组件,包括 <ClientOnly>
和 <DevOnly>
。你可以在 API 文档中阅读更多相关信息。
¥There are a number of components that Nuxt provides, including <ClientOnly>
and <DevOnly>
. You can read more about them in the API documentation.
:
::
库作者
¥Library Authors
创建具有自动 tree-shaking 和组件注册功能的 Vue 组件库非常简单。✨
¥Making Vue component libraries with automatic tree-shaking and component registration is super easy. ✨
你可以使用 @nuxt/kit
提供的 addComponentsDir
方法在 Nuxt 模块中注册你的组件目录。
¥You can use the addComponentsDir
method provided from the @nuxt/kit
to register your components directory in your Nuxt module.
设想一个这样的目录结构:
¥Imagine a directory structure like this:
-| node_modules/
---| awesome-ui/
-----| components/
-------| Alert.vue
-------| Button.vue
-----| nuxt.ts
-| pages/
---| index.vue
-| nuxt.config.ts
然后在 awesome-ui/nuxt.ts
中,你可以使用 addComponentsDir
钩子:
¥Then in awesome-ui/nuxt.ts
you can use the addComponentsDir
hook:
import { createResolver, defineNuxtModule, addComponentsDir } 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',
})
},
})
就是这样!现在,在你的项目中,你可以将 UI 库作为 Nuxt 模块导入到 nuxt.config
文件中:
¥That's it! Now in your project, you can import your UI library as a Nuxt module in your nuxt.config
file:
export default defineNuxtConfig({
modules: ['awesome-ui/nuxt']
})
...并直接在我们的 pages/index.vue
中使用模块组件(以 awesome-
为前缀):
¥... and directly use the module components (prefixed with awesome-
) in our pages/index.vue
:
<template>
<div>
My <AwesomeButton>UI button</AwesomeButton>!
<awesome-alert>Here's an alert!</awesome-alert>
</div>
</template>
它将仅在使用时自动导入组件,并且在 node_modules/awesome-ui/components/
中更新组件时支持 HMR。
¥It will automatically import the components only if used and also support HMR when updating your components in node_modules/awesome-ui/components/
.