nuxt3目录结构详解
在 Nuxt.js 3 中,一个应用程序的文件夹结构具有一定的规范性。以下是 Nuxt.js 3 的文件夹结构及其用途的详细解释:
.nuxt 目录
Nuxt 使用.nuxt/
目录在开发中生成您的Vue应用程序。
你不应该碰里面的任何文件,因为整个目录将在运行nuxt dev
时重新创建。
.output 目录
Nuxt在为生产构建应用程序时创建.output
目录。
你不应该碰里面的任何文件,因为整个目录将在运行nuxt build
时重新创建。
使用此目录将Nuxt应用程序部署到生产环境。
assets 目录
assets/
目录用于添加构建工具(webpack或Vite)将处理的所有网站资产。
该目录通常包含以下类型的文件:
- Stylesheets (CSS, SASS, etc.)
- Fonts
- Images 它不会从
public/
目录中提供。
如果你想从服务器上提供资产,我们建议看一下public/
目录
Components 目录
components/
目录是您放置所有Vue组件的地方,然后可以在您的页面或其他组件中导入这些组件
Nuxt自动导入你的components
目录中的任何组件(以及你可能正在使用的任何模块注册的组件)。
| components/
--| TheHeader.vue
--| TheFooter.vue
代码语言:javascript复制layouts/default.vue
<template>
<div>
<TheHeader />
<slot />
<TheFooter />
</div>
</template>
Custom directories
默认情况下,只扫描~/components目录。如果要添加其他目录,或更改在该目录的子文件夹中扫描组件的方式,可以向配置中添加其他目录:
代码语言:javascript复制nuxt.config.ts
export default defineNuxtConfig({
components: [
{ path: '~/components/special-components', prefix: 'Special' },
'~/components'
]
})
Any nested directories need to be added first as they are scanned in order. 任何嵌套的目录都需要在按顺序扫描时先添加。
Component extensions
默认情况下,在nuxt.config.ts
的扩展键中指定了扩展名的任何文件都被视为组件。如果需要限制应注册为组件的文件扩展名,可以使用组件目录声明的扩展形式及其扩展键:
export default defineNuxtModule({
components: [
{
path: '~/components',
extensions: ['.vue'],
}
]
})
Component Names
如果你在嵌套目录中有一个组件,例如:
代码语言:javascript复制| components/
--| base/
----| foo/
------| Button.vue
然后组件的名称将基于它自己的路径、目录和文件名,删除重复的段。因此,组件的名称将是:
代码语言:javascript复制<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>
。
Dynamic Components
如果你想使用Vue <component :is="someComputedComponent">
语法,那么你将需要使用Vue提供的resolveComponent
辅助方法。
例如:
代码语言:javascript复制<template>
<component :is="clickable ? MyButton : 'div'" />
</template>
<script setup>
const MyButton = resolveComponent('MyButton')
</script>
如果你正在使用resolveComponent
来处理动态组件,请确保除了组件名称之外不插入任何内容,组件名称必须是字符串而不是变量。
或者,尽管不推荐,您可以全局注册所有组件,这将为所有组件创建异步块,并使它们在整个应用程序中可用。
代码语言:javascript复制export default defineNuxtConfig({
components: {
global: true,
dirs: ['~/components']
},
})
您还可以通过将某些组件放在 ~/components/global
目录中来选择性地全局注册它们。
global
选项也可以为每个组件目录设置。
Dynamic Imports
要动态导入一个组件(也称为惰性加载组件),你所需要做的就是在组件名称前添加Lazy
前缀。
layouts/default.vue
<template>
<div>
<TheHeader />
<slot />
<LazyTheFooter />
</div>
</template>
如果不总是需要该组件,这尤其有用。通过使用Lazy
前缀,你可以延迟加载组件代码,直到合适的时刻,这有助于优化你的JavaScript包大小。
pages/index.vue
<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>
Direct Imports
如果你想要或需要绕过Nuxt的自动导入功能,你也可以显式地从#components
导入组件。
pages/index.vue
<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>
ClientOnly Component
Nuxt 提供了<ClientOnly>
组件,目的是只在客户端呈现一个组件。若要只在客户端导入组件,请在客户端插件中注册该组件。
pages/example.vue
<template>
<div>
<Sidebar />
<ClientOnly>
<!-- this component will only be rendered on client-side -->
<Comments />
</ClientOnly>
</div>
</template>
使用槽位作为回退,直到<ClientOnly>
挂载到客户端。
pages/example.vue
<template>
<div>
<Sidebar />
<!-- This renders the "span" element on the server side -->
<ClientOnly fallbackTag="span">
<!-- this component will only be rendered on client side -->
<Comments />
<template #fallback>
<!-- this will be rendered on server side -->
<p>Loading comments...</p>
</template>
</ClientOnly>
</div>
</template>
.client Components
如果组件只能在客户端呈现,则可以添加.client
后缀到您的组件。
| components/
--| Comments.client.vue
代码语言:javascript复制pages/example.vue
<template>
<div>
<!-- this component will only be rendered on client side -->
<Comments />
</div>
</template>
此功能仅适用于Nuxt自动导入和 #components
导入。从它们的实际路径显式导入这些组件并不会将它们转换为仅针对客户端的组件。
.client
组件只有在被挂载后才被渲染。要使用onMounted()
访问呈现的模板,在 onMounted()
钩子的回调中添加await nextTick()
。
.server Components
.server
组件既可以单独使用,也可以与.client
组件配对使用。
Standalone server components
Standalone server components will always be rendered on the server. When their props update, this will result in a network request that will update the rendered HTML in-place.
A video made by LearnVue for the Nuxt documentation.
Server components are currently experimental and in order to use them, you need to enable the 'component islands' feature in your nuxt.config:
代码语言:javascript复制nuxt.config.ts
export default defineNuxtConfig({
experimental: {
componentIslands: true
}
})
Now you can register server-only components with the .server
suffix and use them anywhere in your application automatically.
| components/
--| HighlightedMarkdown.server.vue
代码语言:javascript复制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>
Slots are not supported by server components in their current state of development.
Paired with a .client component
在这种情况下,.server
.client
组件是组件的两部分,可以在高级用例中用于服务器端和客户端组件的独立实现。
| components/
--| Comments.client.vue
--| Comments.server.vue
代码语言:javascript复制pages/example.vue
<template>
<div>
<!-- this component will render Comments.server server-side then Comments.client once mounted in client-side -->
<Comments />
</div>
</template>
组件的客户端部分能够为服务器呈现的HTML'hydrate'是很重要的。也就是说,它应该在初始加载时呈现相同的HTML,否则您将遇到水合不匹配的情况。
DevOnly Component
Nuxt提供了<DevOnly>
组件,只在开发过程中渲染组件。
这些内容将不包含在生产构建和 tree-shaken中。
代码语言:javascript复制pages/example.vue
<template>
<div>
<Sidebar />
<DevOnly>
<!-- this component will only be rendered during development -->
<LazyDebugBar />
</DevOnly>
</div>
</template>
Library Authors
制作具有自动摇树和组件注册功能的Vue组件库非常简单✨
你可以使用components:dirs
钩子来扩展目录列表,而不需要在Nuxt模块中进行用户配置。
想象一个这样的目录结构:
代码语言:javascript复制| 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 } from '@nuxt/kit'
import { fileURLToPath } from 'node:url'
export default defineNuxtModule({
hooks: {
'components:dirs'(dirs) {
// Add ./components dir to the list
dirs.push({
path: fileURLToPath(new URL('./components', import.meta.url)),
prefix: 'awesome'
})
}
}
})
就是这样!现在在你的项目中,你可以在你的nuxt.config
文件中导入你的UI库作为Nuxt模块:
export default {
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>
It will automatically import the components only if used and also support HMR when updating your components in node_modules/awesome-ui/components/
.
Composables 目录
Nuxt 3使用composables/
目录使用auto-imports自动将Vue组合导入到应用中!
在底层,Nuxt自动生成文件.nuxt/imports.d.ts
来声明类型。
注意,为了让Nuxt生成类型,你必须运行nuxi prepare
, nuxi dev
或 nuxi build
。如果你在没有运行开发服务器的情况下创建了一个可组合对象,TypeScript会抛出一个错误,比如Cannot find name 'useBar'.
Usage
Method 1: Using named export
代码语言:javascript复制composables/useFoo.ts
export const useFoo = () => {
return useState('foo', () => 'bar')
}
Method 2: Using default export
代码语言:javascript复制composables/use-foo.ts or composables/useFoo.ts
// It will be available as useFoo() (camelCase of file name without extension)
export default function () {
return useState('foo', () => 'bar')
}
用法: 你现在可以在 .js
, .ts
和 .vue
文件中使用自动导入组合
app.vue
<template>
<div>
{{ foo }}
</div>
</template>
<script setup>
const foo = useFoo()
</script>
示例
Nested Composables
你可以在使用自动导入的另一个可组合对象中使用一个可组合对象:
代码语言:javascript复制composables/test.ts
export const useFoo = () => {
const nuxtApp = useNuxtApp()
const bar = useBar()
}
访问插件注入
你可以从可组合文件中访问plugin injections
代码语言:javascript复制composables/test.ts
export const useHello = () => {
const nuxtApp = useNuxtApp()
return nuxtApp.$hello
}
如何扫描文件
Nuxt只扫描 composables/
目录的顶层文件,例如:
composables
| - index.ts // scanned
| - useFoo.ts // scanned
| - nested
| --- utils.ts // not scanned
只有composables/index.ts
和 composables/useFoo.ts
会被搜索导入。
为了让自动导入工作于嵌套模块,你可以重新导出它们(推荐)或配置扫描器包含嵌套目录:
示例: 从composables/index.ts
中重新导出您需要的组合的文件:
composables/index.ts
// Enables auto import for this export
export { utils } from './nested/utils.ts'
示例: 扫描composables/
文件夹内的嵌套目录:
nuxt.config.ts
export default defineNuxtConfig({
imports: {
dirs: [
// Scan top-level modules
'composables',
// ... or scan modules nested one level deep with a specific name and file extension
'composables/*/index.{ts,js,mjs,mts}',
// ... or scan all modules within given directory
'composables/**'
]
}
})
Content 目录
Nuxt Content模块读取项目中的content/
目录并解析.md
, .yml
, .csv
and .json
文件为您的应用程序创建一个基于文件的CMS。
- 使用内置组件渲染您的内容。
- 使用类似mongodb的API查询您的内容。
- 使用带有MDC语法的Markdown文件中的Vue组件。
- 自动生成导航。
开始
安装
在项目中安装 @nuxt/content
模块:
yarn add --dev @nuxt/content
或
npm install --save-dev @nuxt/content
或
pnpm add -D @nuxt/content
Then, add @nuxt/content
to the modules
section of nuxt.config.ts
:
nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@nuxt/content'
],
content: {
// https://content.nuxtjs.org/api/configuration
}
})
创建内容
Place your markdown files inside the content/
directory in the root directory of your project:
content/index.md
# Hello Content
模块自动加载并解析它们。
渲染页面
要呈现内容页面,使用 ContentDoc
组件添加一个catch-all路由:
pages/[...slug].vue
<template>
<main>
<ContentDoc />
</main>
</template>
Layouts 目录
Nuxt提供了一个可定制的布局框架,可以在整个应用程序中使用,非常适合将常见的UI或代码模式提取到可重用的布局组件中。
布局放在layouts/
目录中,使用时将通过异步导入自动加载。布局是通过添加<NuxtLayout>
到您的app.vue
,或者设置一个layout
属性作为页面元数据的一部分(如果你使用~/pages
集成),或者手动指定它作为<NuxtLayout>
的一个prop。(注意: 布局名称被规范化为串格式,因此 someLayout
变成some-layout
。)
如果你的应用只有一个布局,我们建议使用app.vue。
不像其他组件,你的布局必须有一个根元素,以允许Nuxt在布局变化之间应用过渡-这个根元素不能是<slot />
。
启用默认布局
Add a ~/layouts/default.vue
:
layouts/default.vue
<template>
<div>
一些在所有页面共享的默认布局
<slot />
</div>
</template>
在布局文件中,布局的内容将加载在<slot />
中,而不是使用特殊的组件。
如果你使用app.vue
你还需要添加 <NuxtLayout>
:
app.vue
<template>
<NuxtLayout>
some page content
</NuxtLayout>
</template>
设置另一种布局
代码语言:javascript复制-| layouts/
---| default.vue
---| custom.vue
你可以像这样直接覆盖默认布局:
代码语言:javascript复制app.vue
<template>
<NuxtLayout :name="layout">
<NuxtPage />
</NuxtLayout>
</template>
<script setup>
// 您可以根据API调用或登录状态来选择此选项
const layout = "custom";
</script>
或者,你可以像这样覆盖默认的每页布局:
代码语言:javascript复制pages/index.vue
<script>
// This will work in both `<script setup>` and `<script>`
definePageMeta({
layout: "custom",
});
</script>
代码语言:javascript复制app.vue
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
代码语言:javascript复制layouts/custom.vue
<template>
<div>
Some *custom* layout
<slot />
</div>
</template>
代码语言:javascript复制layouts/default.vue
<template>
<div>
A *default* layout
<slot />
</div>
</template>
了解更多关于定义页面元数据的信息。
动态更改布局
还可以为布局使用ref或computed属性。
代码语言:javascript复制<template>
<div>
<button @click="enableCustomLayout">Update layout</button>
</div>
</template>
<script setup>
function enableCustomLayout () {
setPageLayout('custom')
}
definePageMeta({
layout: false,
});
</script>
在每页的基础上重写布局
如果您正在使用~/pages
集成,您可以通过设置layout: false
,然后在页面内使用<NuxtLayout>
组件来获得完全控制。
pages/index.vue
<template>
<div>
<NuxtLayout name="custom">
<template #header> Some header template content. </template>
The rest of the page
</NuxtLayout>
</div>
</template>
<script setup>
definePageMeta({
layout: false,
});
</script>
代码语言:javascript复制layouts/custom.vue
<template>
<div>
<header>
<slot name="header">
Default header content
</slot>
</header>
<main>
<slot />
</main>
</div>
</template>
Middleware 目录
Nuxt提供了一个可定制的路由中间件框架,可以在整个应用程序中使用,非常适合在导航到特定路由之前提取想要运行的代码。
路由中间件运行在Nuxt应用程序的Vue部分中。尽管名称相似,但它们与服务器中间件完全不同,服务器中间件运行在应用程序的Nitro服务器部分中。
路由中间件有三种:
- 匿名(或内联)路由中间件,直接在使用它们的页面中定义。
- 命名路由中间件,放置在
middleware/
目录中,在页面上使用时会通过异步导入自动加载。(注意:路由中间件名称被规范化为串串形式,因此someMiddleware
变成some-middleware
。) - 全局路由中间件,放置在
middleware/
目录中(带有.global
后缀),并将在每次路由更改时自动运行。
前两种路由中间件可以在definePageMeta
中定义。
格式
路由中间件是导航守卫,它接收当前路由和下一个路由作为参数。
代码语言:javascript复制export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
}
return navigateTo('/')
})
Nuxt提供了两个全局可用的辅助函数,它们可以直接从中间件返回:
navigateTo (to: RouteLocationRaw | undefined | null, options?: { replace: boolean, redirectCode: number, external: boolean )
- 在插件或中间件中重定向到给定的路由。也可以直接调用它来执行页面导航。abortNavigation (err?: string | Error)
- 终止导航,并显示一条可选的错误消息。
不像vue-router中的导航守卫,第三个next()
参数不会被传递,重定向或路由取消是通过从中间件返回值来处理的。可能的返回值有:
- nothing - 不会阻塞导航,并且会移动到下一个中间件功能(如果有的话),或者完成路由导航
return navigateTo('/')
orreturn navigateTo({ path: '/' })
- 重定向到给定的路径,并将重定向代码设置为302
Found,如果重定向发生在服务器端return navigateTo('/', { redirectCode: 301 })
- 重定向到给定的路径,并将重定向代码设置为301
Moved permanent,如果重定向发生在服务器端return abortNavigation()
- 停止当前导航return abortNavigation(error)
- 拒绝有错误的当前导航
我们建议使用上面的帮助函数来执行重定向或停止导航。vue-router中描述的其他可能的返回值可能可以工作,但将来可能会有破坏性的更改。
动态添加中间件
可以使用addRouteMiddleware()
辅助函数手动添加全局或命名路由中间件,例如从插件中添加。
export default defineNuxtPlugin(() => {
addRouteMiddleware('global-test', () => {
console.log('这个全局中间件是在一个插件中添加的,将在每次路由更改时运行')
}, { global: true })
addRouteMiddleware('named-test', () => {
console.log('这个命名中间件是在插件中添加的,它将覆盖任何同名的现有中间件')
})
})
示例:命名路由中间件
代码语言:javascript复制-| middleware/
---| auth.ts
在页面文件中,可以引用这个路由中间件
代码语言:javascript复制<script setup>
definePageMeta({
middleware: ["auth"]
// or middleware: 'auth'
})
</script>
现在,在导航到该页面完成之前, auth
路由中间件将运行。
Node modules 目录
包管理器(npm
或yarn
或pnpm
)创建node_modules/
目录来存储项目的依赖项。
Pages 目录
Nuxt提供了一个基于文件的路由,在您的web应用程序中使用Vue Router在底层创建路由。
这个目录是 可选的 ,这意味着如果你只使用app.vue
, vue-router
不会被包括在内(除非你在nuxt.config
中设置了pages: true
或者有一个app/router.options.ts
),减少应用程序的包大小。
使用
页面是Vue组件,可以使用 .vue
, .js
, .jsx
, .ts
or .tsx
扩展名
pages/index.vue
<template>
<h1>Index page</h1>
</template>
代码语言:javascript复制// https://vuejs.org/guide/extras/render-function.html
export default defineComponent({
render () {
return h('h1', 'Index page')
}
})
代码语言:javascript复制// https://nuxt.com/docs/examples/advanced/jsx
// https://vuejs.org/guide/extras/render-function.html#jsx-tsx
export default defineComponent({
render () {
return <h1>Index page</h1>
}
})
The pages/index.vue
file will be mapped to the /
route of your application.
如果你正在使用app.vue,确保使用<NuxtPage/>
组件来显示当前页面:
app.vue
<template>
<div>
<!-- Markup shared across all pages, ex: NavBar -->
<NuxtPage />
</div>
</template>
Pages必须有一个根元素,以允许页面之间的路由transitions。(HTML注释也被认为是元素。)
这意味着当路由被服务器渲染或静态生成时,您将能够正确地看到它的内容,但是当您在客户端导航期间导航到该路由时,路由之间的转换将失败,您将看到路由将不会被渲染。
下面是一些例子来说明只有一个根元素的页面是什么样子的:
代码语言:javascript复制pages/working.vue
<template>
<div>
<!-- 这个页面正确地只有一个根元素 -->
Page content
</div>
</template>
代码语言:javascript复制pages/bad-1.vue
<template>
<!-- 由于此注释,当客户端导航期间路由更改时,此页面将不会呈现 -->
<div>Page content</div>
</template>
代码语言:javascript复制pages/bad-2.vue
<template>
<div>This page</div>
<div>有多个根元素</div>
<div>并且不会在客户端导航期间的路由更改时呈现</div>
</template>
动态 Routes
如果您将任何内容放在方括号内,它将被转换为dynamic route参数。您可以在文件名或目录中混合和匹配多个参数,甚至是非动态文本。
If you want a parameter to be optional, you must enclose it in double square brackets - for example, ~/pages/[[slug]]/index.vue
or ~/pages/[[slug]].vue
will match both /
and /test
. 如果您希望参数是 可选的 ,则必须将其括在双方括号中——例如,~/pages/[[slug]]/index.vue
或 ~/pages/[[slug]].vue
将同时匹配 /
和 /test
。
示例
代码语言:javascript复制-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue
根据上面的例子,你可以通过$route
对象访问组件中的 group/id:
pages/users-[group]/[id].vue
<template>
<p>{{ $route.params.group }} - {{ $route.params.id }}</p>
</template>
导航到/users-admins/123
将呈现:
<p>admins - 123</p>
如果你想使用Composition API访问路由,有一个全局的useRoute
函数,它将允许你访问路由,就像选项API中的this.$route
一样。
<script setup>
const route = useRoute()
if (route.params.group === 'admins' && !route.params.id) {
console.log('Warning! Make sure user is authenticated!')
}
</script>
Catch-all Route
If you need a catch-all route, you create it by using a file named like [...slug].vue. This will match all routes under that path.
代码语言:javascript复制pages/[...slug].vue
<template>
<p>{{ $route.params.slug }}</p>
</template>
导航到/hello/world
将呈现:
<p>["hello", "world"]</p>
嵌套 Routes
可以使用 <NuxtPage>
来显示嵌套路由。
示例:
代码语言:javascript复制-| pages/
---| parent/
------| child.vue
---| parent.vue
这个文件树将生成这些路由:
代码语言:javascript复制[
{
path: '/parent',
component: '~/pages/parent.vue',
name: 'parent',
children: [
{
path: 'child',
component: '~/pages/parent/child.vue',
name: 'parent-child'
}
]
}
]
要显示child.vue
组件,你必须在pages/parent.vue
中插入<NuxtPage>
组件:
pages/parent.vue
<template>
<div>
<h1>I am the parent view</h1>
<NuxtPage :foobar="123" />
</div>
</template>
Child Route Keys
如果你想要更多的控制<NuxtPage>
组件被重新渲染(例如,对于transitions),你可以通过pageKey
道具传递一个字符串或函数,或者你可以通过definePageMeta
定义一个key
值:
pages/parent.vue
<template>
<div>
<h1>I am the parent view</h1>
<NuxtPage :page-key="someKey" />
</div>
</template>
换个:
代码语言:javascript复制pages/child.vue
<script setup>
definePageMeta({
key: route => route.fullPath
})
</script>
Page Metadata
你可能想在你的应用程序中为每个路由定义元数据。你可以使用definePageMeta
宏来实现这一点,它将在<script>
和<script setup>
中工作:
<script setup>
definePageMeta({
title: 'My home page'
})
</script>
这些数据可以通过route.meta
对象在应用程序的其余部分中访问。
<script setup>
const route = useRoute()
console.log(route.meta.title) // My home page
</script>
如果使用嵌套路由,那么来自所有这些路由的页面元数据将被合并到单个对象中。有关路由元的更多信息,请参见vue-router docs。
类似于definetriggers
或defineProps
(参见Vue docs), definePageMeta
是一个编译器宏。它将被编译掉,因此您不能在组件中引用它。相反,传递给它的元数据将从组件中提升出来。因此,页面元对象不能引用组件(或组件上定义的值)。但是,它可以引用导入的绑定。
<script setup>
import { someData } from '~/utils/example'
const title = ref('')
definePageMeta({
title, // This will create an error
someData
})
</script>
Special Metadata
当然,你可以在整个应用程序中定义元数据供自己使用。但是一些用definePageMeta
定义的元数据有一个特定的目的:
alias
您可以定义页面别名。它们允许您从不同的路径访问同一个页面。它可以是字符串,也可以是vue-router文档中定义的字符串数组(https://router.vuejs.org/guide/essentials/redirect-and-alias.html#alias)。
keepalive
如果你在你的definePageMeta
中设置KeepAlive: true
, Nuxt将自动包装你的页面Vue 组件。例如,如果您希望跨路由更改保持页面状态,那么在具有动态子路由的父路由中这样做可能很有用。
当你的目标是为父路由保留状态时,使用以下语法:<NuxtPage keepalive />
。你也可以设置传递给' '的道具(查看完整列表这里)。
你可以为这个属性设置一个默认值在你的nuxt.config
中。
key
See above.
layout
您可以定义用于呈现路由的布局。这可以是false(禁用任何布局),一个字符串或一个ref/computed,如果你想让它以某种方式响应。关于布局的更多信息。
layoutTransition and pageTransition
你可以为包装页面和布局的<transition>
组件定义转换属性,或者传递false
来禁用该路由的<transition>
包装。您可以在这里看到可传递的选项列表,或者阅读关于过渡如何工作的更多信息。
你可以为这些属性设置默认值在你的nuxt.config
中。
middleware
可以在加载此页面之前定义要应用的中间件。它将与任何匹配的父/子路由中使用的所有其他中间件合并。它可以是字符串、函数(遵循全局前保护模式的匿名/内联中间件函数)或字符串/函数数组。关于命名中间件的更多信息。
name
您可以为该页的路由定义一个名称。
path
如果您有一个比文件名更复杂的模式,您可以定义一个路径匹配器。更多信息请参见vue-router
文档。
Typing Custom Metadata
如果要为页面添加自定义元数据,您可能希望以类型安全的方式这样做。可以增加definePageMeta
接受的对象的类型:
index.d.ts
declare module '#app' {
interface PageMeta {
pageType?: string
}
}
// 在扩充类型时,确保导入/导出某些内容总是很重要的
export {}
页面导航
要在应用程序的页面之间导航,你应该使用<NuxtLink>
组件。
该组件包含在Nuxt中,因此您不必像导入其他组件那样导入它。
一个简单的链接到你的pages文件夹中的index.vue页面:
代码语言:javascript复制<template>
<NuxtLink to="/">Home page</NuxtLink>
</template>
Router 选项
可以自定义vue-router选项。
Using app/router.options
这是指定路由器选项的推荐方法。
代码语言:javascript复制app/router.options.ts
import type { RouterConfig } from '@nuxt/schema'
// https://router.vuejs.org/api/interfaces/routeroptions.html
export default <RouterConfig> {
}
自定义 Routes
您可以选择使用一个接受扫描路由并返回定制路由的函数来覆盖路由。 如果返回null
或 undefined
, Nuxt将退回到默认路由。(用于修改输入数组)
app/router.options.ts
import type { RouterConfig } from '@nuxt/schema'
// https://router.vuejs.org/api/interfaces/routeroptions.html
export default <RouterConfig> {
routes: (_routes) => [
{
name: 'home',
path: '/',
component: () => import('~/pages/home.vue')
}
],
}
自定义历史记录(高级)
您可以选择使用接受基url并返回历史模式的函数来覆盖历史模式。 如果返回null
or undefined
, Nuxt将退回到默认的历史记录。
app/router.options.ts
import type { RouterConfig } from '@nuxt/schema'
import { createMemoryHistory } from 'vue-router'
// https://router.vuejs.org/api/interfaces/routeroptions.html
export default <RouterConfig> {
history: base => process.client ? createMemoryHistory(base) : null /* default */
}
使用 nuxt.config
注意: 只有JSON可序列化选项是可配置的:
linkActiveClass
linkExactActiveClass
end
sensitive
strict
hashMode
nuxt.config.ts
export default defineNuxtConfig({
router: {
// https://router.vuejs.org/api/interfaces/routeroptions.html
options: {}
}
})
Hash 模式 (SPA)
您可以在SPA模式下启用哈希历史。在这种模式下,路由器在内部传递的实际URL之前使用一个哈希字符(#)。当启用时,URL永远不会发送到服务器,SSR不支持。
代码语言:javascript复制nuxt.config.ts
export default defineNuxtConfig({
ssr: false,
router: {
options: {
hashMode: true
}
}
})
Programmatic Navigation
Nuxt 3允许通过navigateTo()
实用方法进行编程导航。使用此实用工具方法,您将能够在应用程序中以编程方式导航用户。这对于从用户获取输入并在整个应用程序中动态导航用户非常有用。在本例中,我们有一个名为navigateTo()
的简单方法,当用户提交搜索表单时调用它。
注意: 确保在navigateTo
上总是await
,或者通过从函数返回来链接它的结果。
<script setup>
const router = useRouter();
const name = ref('');
const type = ref(1);
function navigate(){
return navigateTo({
path: '/search',
query: {
name: name.value,
type: type.value
}
})
}
</script>
Plugins 目录
Nuxt自动读取您的plugins
目录中的文件,并在创建Vue应用程序时加载它们。你可以在文件名中使用.server
或.client
后缀来只在服务器端或客户端加载插件。
plugins/
目录下的所有插件都是自动注册的,所以你不应该将它们单独添加到你的nuxt.config
目录中。
注册了哪些文件
只有在plugins/
目录的顶层的文件(或任何子目录中的索引文件)才会被注册为插件。
例如:
代码语言:javascript复制plugins
| - myPlugin.ts
| - myOtherPlugin
| --- supportingFile.ts
| --- componentToRegister.vue
| --- index.ts
只有myPlugin.ts
和 myOtherPlugin/index.ts
会被注册。
创建组件
传递给插件的唯一参数是 nuxtApp
.
export default defineNuxtPlugin(nuxtApp => {
// Doing something with nuxtApp
})
插件注册令
您可以通过在文件名前面加上一个数字来控制插件注册的顺序。
例如:
代码语言:javascript复制plugins/
| - 1.myPlugin.ts
| - 2.myOtherPlugin.ts
在本例中,2.myOtherPlugin.ts
将能够访问1.myPlugin.ts
注入的任何内容。
这在一个插件依赖于另一个插件的情况下非常有用。
在插件中使用可组合文件
你可以在 Nuxt plugins中使用composables:
代码语言:javascript复制export default defineNuxtPlugin((NuxtApp) => {
const foo = useFoo()
})
然而,请记住有一些限制和区别:
- 如果一个可组合的插件依赖于后来注册的另一个插件,它可能无法工作
- 原因: 插件按顺序调用,先于所有其他插件。你可以使用一个依赖于另一个尚未被调用的插件的可组合。
- 如果一个可组合文件依赖于Vue.js的生命周期,它将无法工作
- 原因: 通常情况下,Vue.js组合组件被绑定到当前组件实例,而插件只被绑定到
nuxtApp
实例。
自动提供辅助函数
如果你想在NuxtApp
实例上提供一个帮助器,请在provide
键下从插件中返回它。例如:
export default defineNuxtPlugin(() => {
return {
provide: {
hello: (msg: string) => `Hello ${msg}!`
}
}
})
在另一个文件中,你可以这样做:
代码语言:javascript复制<template>
<div>
{{ $hello('world') }}
</div>
</template>
<script setup lang="ts">
// alternatively, you can also use it here
const { $hello } = useNuxtApp()
</script>
输入插件
如果你从插件中返回你的helper,它们会自动被输入;你会发现它们为返回的useNuxtApp()
和在你的模板中键入。
如果你 需要 在另一个插件中使用提供的帮助程序,你可以调用useNuxtApp()
来获得类型化版本。但通常情况下,应该避免这样做,除非您确定插件的顺序。
Advanced
对于高级用例,你可以像这样声明注入属性的类型:
代码语言:javascript复制index.d.ts
declare module '#app' {
interface NuxtApp {
$hello (msg: string): string
}
}
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$hello (msg: string): string
}
}
export { }
Vue 插件
如果你想使用Vue插件,比如vue-gtag来添加谷歌Analytics标签,你可以使用Nuxt插件来做到这一点。
有一个开放的RFC使这更容易! 查看 nuxt/nuxt#17143
首先,安装你想要的插件。
代码语言:javascript复制yarn add --dev vue-gtag-next
然后创建一个插件文件plugins/vue-gtag.client.js
。
import VueGtag from 'vue-gtag-next'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGtag, {
property: {
id: 'GA_MEASUREMENT_ID'
}
})
})
Vue Diectives
类似地,您可以在插件中注册自定义Vue指令。例如,在 plugins/directive.ts
中:
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive('focus', {
mounted (el) {
el.focus()
},
getSSRProps (binding, vnode) {
// you can provide SSR-specific props here
return {}
}
})
})
Public 目录
public/
目录直接服务于服务器根目录,包含必须保留其名称的公共文件(例如:robots.txt
)或可能不会更改(例如:favicon.ico
)。
Server 目录
Nuxt自动扫描~/server/api
, ~/server/routes
, 和 ~/server/middleware
目录中的文件,以注册具有HMR支持的API和服务器处理程序。
每个文件都应该导出一个用defineEventHandler()
定义的默认函数。
处理程序可以直接返回JSON数据,一个Promise
或使用event.node.res.end()
发送响应。
Read more in Nitro Route Handling Docs.
实例
创建一个新文件server/api/hello.ts
:
server/api/hello.ts
export default defineEventHandler((event) => {
return {
api: 'works'
}
})
你现在可以使用await $fetch('/api/hello')
通用地调用这个API。
Server 路由
~/server/api
中的文件在它们的路由中会自动以/api
作为前缀。 对于添加没有/api
前缀的服务器路由,您可以将它们放到 ~/server/routes
目录中。
示例:
代码语言:javascript复制server/routes/hello.ts
export default defineEventHandler(() => 'Hello World!')
对于上面的例子, /hello
路由可以在http://localhost:3000/hello 上访问。
Server 中间件
Nuxt将自动读入~/server/middleware
中的任何文件,为项目创建服务器中间件。
中间件处理程序将在每个请求上运行,然后再运行任何其他服务器路由,以添加或检查标头、记录请求或扩展事件的请求对象。
中间件处理程序不应返回任何内容(也不应关闭或响应请求),而只检查或扩展请求上下文或抛出错误。
示例:
代码语言:javascript复制server/middleware/log.ts
export default defineEventHandler((event) => {
console.log('New request: ' event.node.req.url)
})
代码语言:javascript复制server/middleware/auth.ts
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
Server 插件
Nuxt将自动读取~/server/plugins
目录中的任何文件,并将它们注册为Nitro插件。这允许扩展Nitro的运行时行为并与生命周期事件挂钩。
示例:
代码语言:javascript复制server/plugins/nitroPlugin.ts
export default defineNitroPlugin((nitroApp) => {
console.log('Nitro plugin', nitroApp)
})
Read more in Nitro Plugins.
Server 工具
服务器路由是由unjs/h3提供的,它附带了一组方便的助手。
Read more in Available H3 Request Helpers.
您可以自己在~/server/utils
目录中添加更多的helper。
使用示例
匹配路由参数
服务器路由可以在文件名的括号内使用动态参数,比如/api/hello/[name].ts
并通过event.context.params
访问。
示例:
代码语言:javascript复制server/api/hello/[name].ts
export default defineEventHandler((event) => `Hello, ${event.context.params.name}!`)
你现在可以使用 await $fetch('/api/hello/nuxt')
调用这个API,并得到Hello, nuxt!
。
匹配 HTTP Method
句柄文件名可以用.get
, .post
, .put
, .delete
作为后缀,…匹配请求的HTTP方法。
server/api/test.get.ts
export default defineEventHandler(() => 'Test get handler')
代码语言:javascript复制server/api/test.post.ts
export default defineEventHandler(() => 'Test post handler')
Given the example above, fetching /test
with:
- GET method: Returns
Test get handler
- POST method: Returns
Test post handler
- Any other method: Returns 405 error
Catch-all Route
全覆盖路由有助于处理备用路由。例如,创建一个名为~/server/api/foo/[...].ts
的文件,将为所有不匹配任何路由处理程序的请求注册一个catch-all路由,例如/api/foo/bar/baz
。
示例:
代码语言:javascript复制server/api/foo/[...].ts
export default defineEventHandler(() => `Default foo handler`)
代码语言:javascript复制server/api/[...].ts
export default defineEventHandler(() => `Default api handler`)
用Body处理请求
代码语言:javascript复制server/api/submit.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
你现在可以使用$fetch('/api/submit', { method: 'post', body: { test: 123 } })
通用地调用这个API。
我们在文件名中使用submit.post.ts
只是为了匹配能够接受请求体的POST
方法的请求。当在GET请求中使用readBody
时,readBody
将抛出405 Method Not Allowed
HTTP错误。
处理带有Query参数的请求
Sample query /api/query?param1=a¶m2=b
server/api/query.get.ts
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.param1, b: query.param2 }
})
错误处理
如果没有抛出错误,则返回状态代码200 OK
。任何未捕获的错误将返回一个500 Internal Server Error
HTTP错误。
要返回其他错误代码,请抛出带有 createError
的异常。
server/api/validation/[id].ts
export default defineEventHandler((event) => {
const id = parseInt(event.context.params.id) as number
if (!Number.isInteger(id)) {
throw createError({
statusCode: 400,
statusMessage: 'ID should be an integer',
})
}
return 'All good'
})
返回其他状态码
要返回其他状态码,可以使用setResponseStatus
。
For example, to return 202 Accepted
server/api/validation/[id].ts
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
访问 Runtime Config
代码语言:javascript复制server/api/foo.ts
export default defineEventHandler((event) => {
const config = useRuntimeConfig()
return { key: config.KEY }
})
访问 Request Cookies
代码语言:javascript复制export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
高级用法示例
Nitro 配置
您可以在nuxt.config
中使用nitro
键直接设置nitro配置。
这是一个高级选项。自定义配置可能会影响生产部署,因为当Nitro在Nuxt的小版本中升级时,配置接口可能会随着时间的推移而改变。
代码语言:javascript复制nuxt.config.ts
export default defineNuxtConfig({
// https://nitro.unjs.io/config
nitro: {}
})
使用嵌套路由器
代码语言:javascript复制server/api/hello/[...slug].ts
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Hello World'))
export default useBase('/api/hello', router.handler)
发送 Streams (实验性)
注意: 这是一个实验特性,只在Node.js环境中可用。
代码语言:javascript复制server/api/foo.get.ts
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
返回一个legacy处理程序或中间件
代码语言:javascript复制server/api/legacy.ts
export default (req, res) => {
res.end('Legacy handler')
}
使用unjs/h3可以支持遗留处理程序,但建议尽可能避免使用遗留处理程序。
代码语言:javascript复制server/middleware/legacy.ts
export default (req, res, next) => {
console.log('Legacy middleware')
next()
}
不要将next()
回调与async
或返回Promise
的legacy中间件结合使用!
Server 存储
Nitro提供了一个跨平台存储层。为了配置额外的存储挂载点,你可以使用nitro.storage
。
示例:使用Redis
代码语言:javascript复制nuxt.config.ts
export default defineNuxtConfig({
nitro: {
storage: {
'redis': {
driver: 'redis',
/* redis connector options */
port: 6379, // Redis port
host: "127.0.0.1", // Redis host
username: "", // needs Redis >= 6
password: "",
db: 0, // Defaults to 0
tls: {} // tls/ssl
}
}
}
})
创建一个新文件 server/api/test.post.ts
:
server/api/test.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
await useStorage().setItem('redis:test', body)
return 'Data is set'
})
创建一个新文件 server/api/test.get.ts
:
server/api/test.get.ts
export default defineEventHandler(async (event) => {
const data = await useStorage().getItem('redis:test')
return data
})
创建一个新文件 app.vue
:
app.vue
<template>
<div>
<div>Post state: {{ resDataSuccess }}</div>
<div>Get Data: {{ resData.text }}</div>
</div>
</template>
<script setup lang="ts">
const { data: resDataSuccess } = await useFetch('/api/test', {
method: 'post',
body: { text: 'Nuxt is Awesome!' }
})
const { data: resData } = await useFetch('/api/test')
</script>
Utils 目录
Nuxt 3 使用 utils/
目录在整个应用程序中使用auto-imports自动导入辅助函数和其他实用程序!
utils/
目录的主要目的是允许在Vue组合函数和其他自动导入的实用函数之间进行语义区分。utils/
自动导入的工作方式和被扫描的方式与组合文件/目录相同。你可以在文档的那个部分看到例子和更多关于它们如何工作的信息。
.env文件
Nuxt CLI在开发模式下以及运行nuxi build
和nuxi generate
时内置了dotenv支持。
除了任何进程环境变量外,如果您的项目根目录中有一个.env
文件,它将在构建、开发和生成时自动加载,并且在nuxt.config
文件和模块中设置的任何环境变量都将可访问。
如果您想使用不同的文件 - 例如,使用.env.local
或.env.production
- 您可以在使用nuxi
时传递--dotenv
标志来实现。例如:
npx nuxi dev --dotenv .env.local
与上面一样,这仅适用于开发模式以及运行nuxi build
和nuxi generate
。
在开发模式下更新.env
文件时,Nuxt实例会自动重新启动以将新值应用于process.env
。
请注意,从.env
文件中删除变量或完全删除.env
文件将不会取消已设置的值。
但是,在构建服务器之后,您需要在运行服务器时自己设置环境变量。此时将不会读取您的.env
文件。如何设置环境变量因每个环境而异。在Linux服务器上,您可以使用终端DATABASE_HOST=mydatabaseconnectionstring node .output/server/index.mjs
传递环境变量作为参数。或者您可以使用source .env && node .output/server/index.mjs
引用您的env文件。
请注意,对于纯静态站点,在项目预渲染之后无法设置运行时配置。
如果您想在构建时使用环境变量但不关心以后更新这些变量(或者只需要在应用程序内部以反应方式更新它们),则appConfig
可能是更好的选择。您可以在您的nuxt.config
中定义appConfig
(使用环境变量),也可以在您的项目中的~/app.config.ts
文件中定义appConfig
。
.gitignore 文件
一个.gitignore
的文件指定了git应该忽略的未跟踪文件。在git文档中了解更多信息。
我们建议有一个 .gitignore
的文件,包含至少以下条目:
.gitignore
# Nuxt dev/build outputs
.output
.nuxt
# Node dependencies
node_modules
# System files
*.log
nuxtignore 文件
在构建阶段,.nuxtignore
文件可以让Nuxt忽略项目根目录(rootDir
)中的layouts
, pages
, components
, composables
和 middleware
文件。.nuxtignore
文件遵循与.gitignore
和 .eslintignore
文件相同的规范,其中每一行都是一个glob模式,指示应该忽略哪些文件。
注意: 你也可以在你的nuxt.config
中配置ignoreOptions
, ignorePrefix
和ignore
。
示例
代码语言:javascript复制.nuxtignore
# ignore layout foo.vue
layouts/foo.vue
# ignore layout files whose name ends with -ignore.vue
layouts/*-ignore.vue
# ignore page bar.vue
pages/bar.vue
# ignore page inside ignore folder
pages/ignore/*.vue
# ignore route middleware files under foo folder except foo/bar.js
middleware/foo/*.js
!middleware/foo/bar.js
App Config File
Nuxt 3提供了一个app.config
配置文件公开应用程序中的响应性配置,能够在生命周期内的运行时更新它,或者使用nuxt插件并使用HMR(热模块替换)编辑它。
你可以使用app.config.ts
文件 轻松提供运行时应用配置。它可以有.ts
, .js
, or .mjs
的扩展。
app.config.ts
export default defineAppConfig({
foo: 'bar'
})
不要把任何秘密的值在app.config
文件。它公开给用户客户端。
定义应用程序配置
To expose config and environment variables to the rest of your app, you will need to define configuration in app.config
file. 要将配置和环境变量暴露给应用程序的其余部分,你需要在'app.config
文件配置。
示例:
代码语言:javascript复制app.config.ts
export default defineAppConfig({
theme: {
primaryColor: '#ababab'
}
})
当添加'theme'到app.config
, Nuxt使用Vite或webpack来捆绑代码。我们可以使用useAppConfig在服务器和浏览器中通用地访问theme
。
const appConfig = useAppConfig()
console.log(appConfig.theme)
手动输入App配置
Nuxt尝试从提供的应用程序配置自动生成一个typescript接口。
也可以手动输入app config:
代码语言:javascript复制index.d.ts
declare module '@nuxt/schema' {
interface AppConfigInput {
/** Theme configuration */
theme?: {
/** Primary app color */
primaryColor?: string
}
}
}
// 在扩充类型时,确保导入/导出某些内容总是很重要的
export {}
app.vue 文件
app.vue
文件是Nuxt 3应用程序中的主要组件。
最小的使用
在Nuxt 3中,pages/目录是可选的。如果不存在,Nuxt将不包含vue-router依赖项。这在处理着陆页面或不需要路由的应用程序时非常有用。
代码语言:javascript复制app.vue
<template>
<h1>Hello World!</h1>
</template>
使用Pages
如果你有一个pages/
目录,使用<NuxtPage>
组件来显示当前页面:
app.vue
<template>
<div>
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</div>
</template>
因为Nuxt 3在<NuxtPage>
使用suspense
,所以它不能被设置为根元素。
记住那个 app.vue
作为Nuxt应用程序的主要组件。你添加的任何东西(JS和CSS)都是全局的,包含在每个页面中。
如果你想在页面之间自定义页面结构,请查看layouts/
目录。
nuxt.Config.ts 文件
Nuxt可以用一个nuxt.config
文件轻松配置,该文件可以有js
, ts
or mjs
扩展名。
export default defineNuxtConfig({
// My Nuxt config
})
defineNuxtConfig
辅助函数是全局可用的,无需导入。
如果你喜欢,你可以显式地从nuxt/config
导入defineNuxtConfig
:
import { defineNuxtConfig } from 'nuxt/config'
export default defineNuxtConfig({
// My Nuxt config
})
为了确保您的配置是最新的,当检测到主配置文件env
, nuxtignore
and nuxtrc
dotfile 中的更改时,Nuxt将完全重新启动。
package.json 文件
package.json文件包含应用程序的所有依赖项和脚本。
tsconfig.json 配置文件
Nuxt自动生成一个.nuxt/tsconfig.json文件,包含你在Nuxt项目中使用的解析别名,以及其他合理的默认值。你可以通过在你的项目的根目录中创建一个tsconfig.json获益,它包含以下内容:
代码语言:javascript复制{
"extends": "./.nuxt/tsconfig.json"
}
根据需要,可以自定义该文件的内容。但是,建议不要覆盖target
, module
和moduleResolution
。此外,请注意,如果你需要自定义你的“路径”,这将覆盖自动生成的路径别名。相反,我们建议你将任何路径别名添加到你的nuxt.conf中的alias属性中,在那里它们将被拾取并添加到自动生成的tsconfig
中。