项目初始化
项目创建
npx nuxi init nuxt3-app
npm install
npm run dev
项目目录结构
nuxt-app/
├── .nuxt/ #无需关心
├── .output/ #无需关心
├── assets/ #静态资源
├── components/ #公共组件-自动导入
├── composables/ #公共函数/方法-自动导入
├── content/ #Nuxt-Content相关-暂无需使用
├── layouts/ #布局模板
├── middleware/ #中间件-如路由导航守卫等
├── node_modules/ #依赖项
├── pages/ #相应页面-自动添加到路由
├── plugins/ #插件目录
├── public/ #静态资源-根目录
└── server/ #用于建立任何后端的逻辑如后端 API,这个目录下还包含了 `api`、`server` 和 `middleware` 来区分功能,不具有自动引入
├── api/
├── routes/
└── middleware/
├── .gitignore
├── .nuxtignore
├── app.config.ts #客户端配置
├── app.vue #入口组件
├── nuxt.config.ts #项目配置
├── package.json
└── tsconfig.json #ts配置
路由
页面路由
nuxt3会自动整合vue-router,并且映射pages/目录到应用的routes配置中,既按照pages中的目录文件自动创建相应的路由,如下所示。其中文件夹中index.vue为该路径默认路由组件,而组件/路由的嵌套则和经典的Vue-router类似,不过把router-view以及router-link分别用nuxtPage和nuxtLink进行了替代
//文件路径
pages
-index.vue
-detail
--index.vue
--some.vue
//相应路由
/-index.vue
/detail-/detail/index.vue
/detail/some-/detail/some.vue
页面组件
<template>
<div>
<NuxtLink to="/test">test</NuxtLink> | <!-- 新增 -->
<NuxtLink to="/">根</NuxtLink> <!--新增 -->
<NuxtPage></NuxtPage>
</div>
</template>
动态路由
建立页面文件时,如果命名时将任何内容放在方括号内,它将被转换为路由参数。在文件名或目录中混合和匹配多个参数。
-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue
会生成路由:
{
"routes": [
{
"name": "users-group-id",
"path": "/users-:group()/:id()",
"component": "~/pages/users-[group]/[id].vue"
},
]
}
根据上面的例子,你可以通过 $route 对象中的 params 访问组件中的 group & idx
<template>
<ul class="text-base text-gray-600">
<li>
获取到的 group 是 <span class="text-green-500 text-xl">{{ group }}</span>
</li>
<li>
获取到的 id 是 <span class="text-green-500 text-xl">{{ id }}</span>
</li>
</ul>
</template>
编程式导航
与 vue3.js 一样,在 setup 可以使用 useRouter、useRoute 来获取路由信息。
<script setup>
const route = useRoute();
const router = useRouter();
const { id } = route.params;
console.log(router.getRoutes());
const handlerToHome = () => {
router.push("/");
};
</script>
自定义路由
配置~/app/router.options.ts
在/src目录新增app文件夹,文件夹下建立router.options.ts文件(如果没有使用本文中的/src目录的配置,要在根目录下新增app文件夹)。该文件返回定制路由的函数来覆盖路由,如果返回 null 或 undefined, Nuxt将退回到默认路由。
-| pages/
---| index.vue
import type { RouterConfig } from "@nuxt/schema";
export default <RouterConfig>{
routes: (_routes) => [
{
name: "home",
path: "/home",
component: () => import("~/pages/index.vue"),
},
],
};
访问其他未定义页面路由,比如原本的 /,提示 404;原因是自定义路由完全替换了自动生成的路由。如果我们只是希望在自动导入的路由下,添加自定义路由,应该使用 pages:extend 钩子配置
配置nuxt.config.ts
使用 pages:extend 钩子配置扩展路由
import { NuxtConfig } from "nuxt/config";
import { NuxtPage } from 'nuxt/schema';
export default defineNuxtConfig({
hooks: {
'pages:extend'(pages: NuxtPage[]) {
pages.push({
name: 'error',
path: '/error',
file: '~/error.vue',
// 可以传递 props 到业务组件内
props: {
error: {
statusCode: '500',
statusMessage: '服务器开小差啦,请稍后重试',
},
},
});
},
},
} as NuxtConfig);
布局Layout
默认布局
那些放在layouts/目录下的SFC会被自动加载进来,如果我们创建的SFC名为default.vue,将会被用于项目所有页面中作为布局模板。
layouts/default.vue:
app.vue
<template>
<div>
hello,nuxt3!
<NuxtPage/>
<slot />
</div>
</template>
layout/default.vue
<template>
<div>
通用布局页,default.vue:
<slot />
</div>
</template>
pages/hello.vue
<template>
<div>
hello page
</div>
</template>
默认布局会自动嵌套在app.vue中
自定义布局
如果我们的布局文件名不叫default,而是别的,比如custom.vue,想要使用它们,就必须在某个页面中设置页面属性layout。
custom.vue:
<template>
<div>
内容来自自定义布局页custom.vue!
<slot />
</div>
</template>
可以在helloworld.vue中试试custom这个布局,helloworld.vue:
<script>
export default {
layout: "custom"
}
</script>
NuxtLayout
可以使用NuxtLayout组件结合slots获得完全控制力,同时需要设置组件选项layout: false。
helloworld.vue
<template>
<NuxtLayout name="custom">
<template #header>
<h1>hello page</h1>
</template>
some content...
</NuxtLayout>
</template>
<script>
export default {
layout: false,
};
</script>
修改一下custom.vue
<template>
<div>
内容来自自定义布局页custom.vue!
<slot name="header"/>
<slot />
</div>
</template>
我们甚至能组合多个布局页:
<template>
<div>
<NuxtLayout name="custom">
<template #header>
<h1>hello page</h1>
</template>
some content...
</NuxtLayout>
<NuxtLayout name="default">
some content...
</NuxtLayout>
</div>
</template>
由于需要设置layout选项,所以在这个script标签旁边同时使用
<script> export default {
layout: "custom",
};
</script>
<script setup> // your setup script </script>
组件components
自动导入组件
我们把Vue组件放在components/目录,这些组件可以被用在页面和其他组件中,以往我们使用这些组件需要导入并注册它们,但Nuxt会自动导入components/目录中的任意组件。比如:
| components/
--| TheHeader.vue
--| TheFooter.vue
layouts/default.vue:
<template>
<div>
<TheHeader />
<slot />
<TheFooter />
</div>
</template>
组件名称约定
没有嵌套的组件会以文件名直接导入,但如果存在嵌套关系哪?例如下面的路径:
| components/
--| base/
----| foo/
------| Button.vue
那么组件名称将会基于路径和文件名连起来,比如上面的base/foo/Button.vue注册名称将会是BaseFooButton,将来用起来会像下面这样:
<BaseFooButton />
组件懒加载
如果在组件名前面加上Lazy前缀,则可以按需懒加载该组件,可用于优化打包尺寸。
比如,下面的用法:
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" />
<button v-if="!show" @click="show = true">显示列表</button>
</div>
</template>
<script setup>
import {ref} from 'vue'
const show = ref(false)
</script>
数据获取
nuxt3中提供的数据获取函数有以下四个:
- useFetch
- useLazyFetch
- useAsyncData
- useLazyAsyncData
注意:它们都必须在
setup或生命周期钩子中使用
useAsyncData
在页面、组件或插件中都可以使用useAsyncData获取那些异步数据。比如:
const {
data: Ref<DataT>, // 返回的数据
pending: Ref<boolean>, // 加载状态指示器
refresh: (force?: boolean) => Promise<void>, // 强制刷新函数
error?: any // 请求失败的错误信息
} = useAsyncData(
key: string,// 唯一键用于多次请求结果去重
fn: () => Object,// 返回数值的异步函数
// lazy - 是否在路由之后才请求数据,server - 是否在服务端请求数据
options?: { lazy: boolean, server: boolean }
)
获取待办事项数据,index.vue:
<template>
<div>
<!-- 待办列表 -->
<div v-for="todo in todos" :key="todo.id">
<input type="checkbox" v-model="todo.completed">
<strong>{{todo.title}}</strong>
</div>
</div>
</template>
<script setup lang="ts">
const { data: todos } = await useAsyncData(
'count', () => $fetch('/api/todos'))
</script>
$fetch使用参考ohmyfetch
useLazyAsyncData
该方法等效于useAsyncData,仅仅设置了lazy选项为true,也就是它不会阻塞路由导航,这意味着我们需要处理data为null的情况(或者给data设置一个默认值)
useFetch
页面、组件或者插件中可以使用useFetch获取任意URL资源。
useFetch是对useAsyncData包装,自动生成key同时推断响应类型,用起来更简单。
看下面方法签名,基本完全相同:
const {
data: Ref<DataT>,
pending: Ref<boolean>,
refresh: (force?: boolean) => Promise<void>,
error?: any
} = useFetch(url: string, options?)
useLazyFetch
该方法等效于useFetch,仅设置了lazy选项为true,所以它不会阻塞路由导航,这意味着我们需要处理data为null的情况(或者通过default选购给data设置一个默认值)
最佳实践
只选取需要的数据
由于请求回来的数据会存储在页面payload中,甚至包括那些没有用到的字段,所以文档中明确建议大家只选择那些组件用到的数据,我们可以设置$fetch的pick选项。
比如,下面的用法:
const { data: mountain } = await useFetch('/api/mountains/everest', {
pick: ['title', 'description']
})
状态共享
Nuxt3提供了 useState 创建响应式且服务端友好的跨组件状态共享能力。
useState是服务端友好的ref替换。它的值在服务端渲染(客户端注水的过程中)将被保留并通过唯一key在组件间共享。
方法签名
useState<T>(key: string, init?: () => T): Ref<T>
- key:唯一键用于去重
- init:提供初始值的函数
useState实践
声明一个状态,index.vue
const counter = useState("counter", () => Math.round(Math.random() * 1000))
<button @click="counter++">+</button>
{{ counter }}
<button @click="counter--">-</button>
共享状态
我们的全局状态当然想要在组件之间共享,此时可以利用nuxt的composables自动导入特性,把它们定义在composables目录中,这样他们将成为全局类型安全的状态。
composables/useCounter.ts
export const useCounter = () =>
useState("counter", () => Math.round(Math.random() * 1000));
配置
nuxt3项目中的相关配置主要放在项目根目录下的nuxt.config.ts文件中,除了nuxt3框架需要的配置属性外,你也可以扩展添加自已处理的配置。 默认情况下,配置文件是这样的。
export default defineNuxtConfig({
// My Nuxt config
})
运行时配置
runtimeConfig接口的作用与环境变量类似,在项目中可以使用此接口暴露出来的变量。默认情况下,这些变量只能在服务端使用,但在runtimeConfig.public下的配置也可以在客户端中使用。 这些变量的值需要定义在nuxt.config中,并且可以被环境变量覆盖。 例如:
export default defineNuxtConfig({
runtimeConfig: {
// The private keys which are only available server-side
apiSecret: '123',
// Keys within public are also exposed client-side
public: {
apiBase: '/api'
}
}
})
环境变量覆盖
# This will override the value of apiSecret
NUXT_API_SECRET=api_secret_token
应用配置
app.config.ts文件默认在项目的根目录下,里头的配置主要是在项目的构建和编译阶段中会使用到。与运行时的配置相反,这里的配置不能被环境变量覆盖。
配置文件中最简单的内容就是导出一个defineAppConfig方法,此方法中有一个你配置对象。
// app.config.ts
export default defineAppConfig({
title: 'Hello Nuxt',
theme: {
dark: true,
colors: {
primary: '#ff0000'
}
}
})
这些变量可以在项目中通过useAppConfig接口来使用.
<script setup>
const appConfig = useAppConfig()
</script>
runtimeConfig与app.config
这两个的作用都是暴露变量给项目中使用。那在实际项目开发过程中到底使用哪个呢。 runtimeConfig: 项目中需要使用指定的私有和仅有的tokes时 app.config: 可以放一些需要在构建时使用的公共Token, 例如主题变量,标题等不敏感的数据
env配置
.env的变量会打入到process.env中,符合规则的会覆盖runtimeConfig的变量
默认加载 .env 文件的配置
.env 文件内容
NUXT_PUBLIC_API_BASE=http://127.0.0.1:53105/mk
.env.production 文件内容
NUXT_PUBLIC_API_BASE=https://vaebe.top:53104/mk
也许你发现了 NUXT_PUBLIC_API_BASE 这个变量, 看下上边的 设置环境变量 就会发现其实就是把对象字段 key 进行拼接。
所以 API_BASE 对应的就是 runtimeConfig\public 配置里的 apiBase 这个字段,.env 文件的变量应该与 runtimeConfig 配置的内容相同。
如果不在 public 下那就应该是 NUXT_API_BASE=xxxxxxx
渲染模式
Nuxt支持不同的渲染模式
- 通用渲染
- 客户端渲染
- 混合渲染
- CDN边缘服务器上渲染
通用渲染
通用渲染模式是服务器端+客户端一起使用的。服务器会首先向浏览器返回一个完整的HTML,这个HTML可能是构建的时候预渲染,也有可能是执行的时候再渲染出来的。这就是服务端渲染的步骤
然后为了不失去客户端渲染的优势,例如动态界面和页面过度,客户端再下载这个HTML之后,还会在后台加载原本在Server上运行的js代码。浏览器会再次解释调用它
这回使得这个静态页面,重新拥有了动态能力。这个过程叫做Hydration,水合作用
服务端渲染的好处
- 性能:用户一下子就能获得完整的内容,响应更加快了
- SEO: 搜索引擎一般只能爬取HTML,不会执行js。所以你的内容更加容易呈现给搜索引擎
服务端渲染的缺点
- 开发麻烦,需要考虑不同环境下的api
- 成本,服务器的成本增加了
客户端渲染
就像是传统的Vue项目一样。打包出来的html会执行main.js。这包含完整的Vue.js代码,然后开始解析用户的操作
好处:
- 开发速度,更快了,而且只有一套浏览器的api
- 便宜,运行成本转嫁给用户了
- 离线,它允许用户下载完完整的代码后,离线运行一时间
缺点
- 性能,用户要等待下载、解析和运行js文件
- SEO不好,同上
Nuxt也提供这种方式
export default defineNuxtConfig({
ssr: false
})
部署一个静态的客户端渲染的应用程序
如果你使用 nuxi generate 或 nuxi build --prerender 命令将应用部署到静态托管服务上,那么默认情况下,Nuxt 会将每个页面渲染为单独的静态 HTML 文件。
如果你只使用客户端渲染,这可能是没有必要的。你只需一个index.html 文件,再加上 200.html 和 404.html 的回退页面,你可以告诉你的静态网页托管服务对所有请求提供这些文件。 为了实现这一点,我们可以改变路由的预渲染方式。只需在你的 nuxt.config.ts 文件中的 hooks 添加以下内容:
export default defineNuxtConfig({
hooks: {
'prerender:routes' ({ routes }) {
routes.clear() // Do not generate any routes (except the defaults)
}
},
})
然后,Nuxt只生成三个文件
index.html200.html404.html
混合渲染
如果你想有的部分使用通用渲染,有部分使用客户端渲染。也是可以的
例如一个CMS管理系统。一些向外展示的页面应该是静态的,但是管理后台需要注册功能,更像一个动态应用。
Nuxt支持在路由规则中设置渲染模式,以支持混合渲染。
export default defineNuxtConfig({
routeRules: {
// 构建的时候就进行预渲染
'/': { prerender: true },
// 产品页面按需生成,在后台重新验证,缓存直到API响应更改
'/products': { swr: true },
// 产品页面按需生成,后台重新验证,缓存1小时(3600秒)
'/products/**': { swr: 3600 },
// 博客文章页面按需生成,在后台重新验证,在CDN上缓存1小时(3600秒)
'/blog': { isr: 3600 },
// 博客文章页面按需生成一次,直到下一次部署,缓存在CDN上
'/blog/**': { isr: true },
// 管理仪表板只在客户端渲染
'/admin/**': { ssr: false },
// 在API路由中添加cors头
'/api/**': { cors: true },
// 重定向遗留url
'/old-page': { redirect: '/new-page' }
}
})
路由规则
你可以使用的不同属性如下:
redirect: string — 定义服务器端重定向。ssr: boolean — 对应用程序的部分区域禁用服务器端渲染,并将其设置为仅单页应用(SPA)模式,通过设置ssr: false实现。cors: boolean — 自动添加跨源资源共享(CORS)头部,通过设置cors: true实现。可以通过覆盖headers属性来定制输出。headers: 对象 — 向网站的特定部分添加特定的头部信息——例如,你的资源文件。swr: 数字或布尔值 — 向服务器响应添加缓存头部,并在服务器或反向代理上为可配置的存活时间(TTL)缓存该响应。Nitro 的 Node 服务器预设能够缓存完整的响应。当 TTL 到期后,会发送已缓存的响应,同时在后台重新生成页面。如果使用true,则会添加一个无过期时间的验证缓存(stale-while-revalidate)头部,但不指定最大有效期(MaxAge)。isr: 数字或布尔值 — 其行为与swr相同,只是我们能够在支持此功能的平台(目前为 Netlify 或 Vercel)上将响应添加到内容分发网络(CDN)缓存中。如果使用true,则内容会在 CDN 中保留到下一次部署。prerender: 布尔值 — 在构建时预先渲染路由,并将它们作为静态资源包含在构建中。experimentalNoScripts: 布尔值 — 对网站的部分区域禁用 Nuxt 脚本和 JavaScript 资源提示的渲染。appMiddleware: 字符串、字符串数组或记录类型(键为字符串,值为布尔值)——允许你定义应该或不应该在 Vue 应用程序部分(即,非 Nitro 路由)的页面路径中运行的中间件。
边缘渲染
这是一个Nuxt引入的强大的功能,它使得Nuxt像静态网站一样支持CDN分发。
边缘侧渲染的工作原理是将渲染过程推送到网络的“边缘”位置,即 CDN 的边缘服务器。需要注意的是,ESR 更多是一种部署目标而非实际的渲染模式。
当请求某个页面时,请求不会直接到达原始服务器,而是被最近的边缘服务器截获。这个服务器会生成页面的 HTML 并将其发送回用户。这一过程减少了数据传输的实际距离,降低了延迟,并加快了页面加载速度
边缘侧渲染得以实现要归功于 Nitro,这是为 Nuxt 3 提供动力的服务器引擎。Nitro 支持跨平台,包括 Node.js、Deno、Cloudflare Workers 等。
目前可以利用边缘侧渲染的平台有:
- 使用 Git 集成和
nuxt build命令,在 Cloudflare Pages 上无需任何配置即可实现边缘侧渲染。 - 使用
nuxt build命令和环境变量NITRO_PRESET=vercel-edge在 Vercel 边缘函数上启用边缘侧渲染。 - 使用
nuxt build命令和环境变量NITRO_PRESET=netlify-edge在 Netlify 边缘函数上启用边缘侧渲染。
值得注意的是,当你使用边缘侧渲染结合路由规则时,可以使用混合渲染(Hybrid Rendering)。
中间件
Nuxt提供了一个可定制的路由中间件框架,您可以在整个应用程序中使用它,非常适合在导航到特定路由之前提取要运行的代码;
路由中间件有三种:
- 匿名(或内联)路由中间件,直接在使用它们的页面中定义。
- 命名路由中间件,放置在中间件/目录中,在页面上使用时将通过异步导入自动加载。(注意:路由中间件的名称被标准化为kebab情况,所以someMiddleware变成了someMiddleware.)
- 全局路由中间件,放置在中间件/目录中(带有.Global后缀),并将在每次路由更改时自动运行
匿名中间件
直接写在页面组件中
<script lang="ts" setup>
export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
}
if (to.path !== '/') {
return navigateTo('/')
}
})
</script>
命名中间件
放在项目的middleware目录下。 此目录下的命名路由如果在page中使用,那nuxt3框架会自动加载此路由中间件。(注意,路由中间件名称会被统一成kebab-case格式,例如: someMiddleware 会变成 some-middleware)
-| middleware/
---| auth.ts
<!-- 在页面文件中的代码 -->
<script setup>
definePageMeta({
middleware: ["auth"]
// or middleware: 'auth'
})
</script>
中间件逻辑
路由中间件以当前路由和下一个路由为参数,如下
export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
}
return navigateTo('/')
})
nuxt3提供两个可直接从中间件返回的方法
-
navigateTo
navigateTo (to: RouteLocationRaw | undefined | null, options?: { replace: boolean, redirectCode: number, external: boolean )此方法可以在中间件或插件中重定向到指定的路由。 也可以直接调用来完成页面的跳转。
-
abortNavigation
abortNavigation (err?: string | Error)这个就是直接终止跳转,并可以返回一些错误信息。
中间件可能的返回值说明:
- 无返回值:也就是说当前中间件不会阻塞路由跳转。
return navigateTo('/')orreturn navigateTo({ path: '/' }), 重定向到指定的路径,如何是在服务端的话,会设置 redirect code 为302return navigateTo('/', { redirectCode: 301 }), 重定向到指定的路径,如果直服务端的话,会设置 redirect code 为301 表示这个重定向的永久的。return abortNavigation()终止当前的跳转return abortNavigation(error)终止跳转并带有错误信息
动态添加中间件
通过addRouteMiddleware() 方法可以在代码中动态添加全局和命名路由中间件。例如在插件中
export default defineNuxtPlugin(() => {
addRouteMiddleware('global-test', () => {
console.log('this global middleware was added in a plugin and will be run on every route change')
}, { global: true })
addRouteMiddleware('named-test', () => {
console.log('this named middleware was added in a plugin and would override any existing middleware of the same name')
})
})
插件
Nuxt3会自动读取plugins目录下的文件并加载它们。我们可以在文件名上使用.server或者.client前缀使他们仅作用于服务端或者客户端。
创建插件
使用defineNuxtPlugin()注册一个插件:
import { defineNuxtPlugin } from '#app'
// 唯一的参数是nuxt实例
export default defineNuxtPlugin(nuxtApp => {
// Doing something with nuxtApp
})
插件用例:自动提供帮助方法
一个常见应用是给NuxtApp实例提供一些额外的帮助方法,我们可以通过编写一个插件,返回一个对象,在里面设置providekey,比如:
import { defineNuxtPlugin } from '#app'
export default defineNuxtPlugin(() => {
return {
provide: {
hello: () => 'world'
}
}
})
使用这个helper,index.vue:
// 会自动加上$前缀
const { $hello } = useNuxtApp();
console.log($hello())
插件用例:访问Vue实例
如果想要扩展Vue,我们通常要访问Vue实例,引入Vue插件。在nuxt3中可以通过插件访问nuxtApp.vueApp.use(xxx)做到。
我们引入vue-devui试一下,前面我们曾经试图自动导入失败了,这里我们手动导入:
npm i vue-devui
创建一个插件vue-devui-plugin.ts:
import { defineNuxtPlugin } from "#app";
import { Button } from "vue-devui";
import 'vue-devui/button/style.css'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(Button);
});
生命周期钩子
- Nuxt Hooks 是在应用构建时 Build Time 可以调用的钩子,一般用于 Nuxt Modules 模块中
- App Hooks 是在前端应用运行时(或 SSR 期间) Runtime 可以调用的钩子,一般用于 Nuxt Plugins 插件中,以便在页面渲染 rendering 时执行特定的操作。也可由用于组件中
- Nitro App Hooks 是在后端服务器运行 Runtime 时可以调用的钩子,一般用于 Nuxt Plugins 插件中,以定制后端引擎 Nitro 运行时的行为
Nuxt Hooks
在应用构建时 Build Time 可以调用的钩子
参考
所有可以调用的 Nuxt Hooks 可以查看官方文档,或源码中所定义的 NuxtHooks schema(数据的架构模式/类型)
可以在配置文件 📄 nuxt.config.ts 的属性 hooks 设置相应的构造时的钩子函数
export default defineNuxtConfig({
hooks: {
// 当 Nuxt 应用实例销毁时调用
'close': () => { }
}
})
也可以在 Nuxt Module 模块中设置相应的构造时的钩子函数
import { defineNuxtModule } from '@nuxt/kit'
// 创建一个模块
export default defineNuxtModule({
// 模块注册时会调用该方法
setup (options, nuxt) {
// 在其中编写模块的逻辑
// 设置一个 close 钩子函数,当 Nuxt 应用实例销毁时调用
nuxt.hook('close', async () => { })
}
})
App Hooks
在应用运行时 Runtime 可以调用的钩子
参考
所有可以调用的 Nuxt Hooks 可以查看官方文档,或源码中所定义的 RuntimeNuxtHooks ❓ schema(数据的架构模式/类型)
可以在 Nuxt Plugin 插件中设置相应的运行时的钩子函数
export default defineNuxtPlugin((nuxtApp) => {
// 设置一个 page:start 钩子函数,当导航进入一个页面,并处于 Suspense 等待时调用
nuxtApp.hook('page:start', () => {
/* your code goes here */
})
})
Nitro App Hooks
在后端服务器运行 Runtime 时可以调用的钩子
参考
所有可以调用的 Nuxt Hooks 可以查看官方文档
可以在服务端的 Nuxt Plugin 插件中设置相应的服务端运行时的钩子函数
// 创建一个后端引擎 Nitro 的插件
export default defineNitroPlugin((nitroApp) => {
// 设置一个 render:html 钩子函数,在生成/渲染 HTML 页面之前调用
nitroApp.hooks.hook('render:html', (html, { event }) => {
console.log('render:html', html)
html.bodyAppend.push('<hr>Appended by custom plugin')
})
// 设置一个 render:response 钩子函数,在后端返回响应数据之前调用
nitroApp.hooks.hook('render:response', (response, { event }) => {
console.log('render:response', response)
})
})
组合API
<script setup lang='ts'>
/****
* Nuxt3组合API
* prefetchComponents
* preloadComponents
* setPageLayout
* etResponseStatus
* updateAppConfig
* useAppConfig
* useAsyncData
* useCookie
* useError
* useFetch
* useHead、
* useNuxtApp
* useRequestHeaders
* useRouter
* useRuntimeConfig
* useState
*
*****/
</script>
1、prefetchComponents可以对预取组件进行控制
使用prefetchComponents手动预取已在 Nuxt 应用程序中全局注册的各个组件。例如:
await prefetchComponents('MyGlobalComponent')
await prefetchComponents(['MyGlobalComponent1', 'MyGlobalComponent2'])
2、preloadComponents可以对预加载组件进行控制
使用preloadComponents手动预取已在 Nuxt 应用程序中全局注册的各个组件。例如:
await preloadComponents('MyGlobalComponent')
await preloadComponents(['MyGlobalComponent1', 'MyGlobalComponent2'])
3、setPageLayout允许动态更改页面的布局。它依赖于对 Nuxt 上下文的访问,并且只能在组件的设置函数、插件和路由中间件中调用。例如
export default defineNuxtRouteMiddleware((to) => {
setPageLayout('other')
})
4、setResponseStatus 设置响应状态
您可以使用setResponseStatus设置响应的 statusCode(以及可选的 statusMessage)。 setResponseStatus只能在组件设置函数、插件和路由中间件中调用。例如:
// 404状态
setResponseStatus(404)
//or
setResponseStatus(404, 'Page Not Found') // 设置状态信息
5、 updateAppConfig更新配置,现有属性被保留。例如:
const appConfig = useAppConfig() // { foo: 'bar' }
const newAppConfig = { foo: 'baz' }
updateAppConfig(newAppConfig)
console.log(appConfig) // { foo: 'baz' }
6、useAppConfig访问配置。例如:
const appConfig = useAppConfig()
console.log(appConfig)
7、 useAsyncData访问异步数据。例如
function useAsyncData(
handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
options?: AsyncDataOptions<DataT>
): AsyncData<DataT>
function useAsyncData(
key: string, //唯一键
handler: (nuxtApp?: NuxtApp) => Promise<DataT>, //返回值异步函数
options?: AsyncDataOptions<DataT>
): Promise<AsyncData<DataT>>
type AsyncDataOptions<DataT> = {
server?: boolean, //是否获取服务器上的数据
lazy?: boolean, //默认false,获取数据之前是否阻塞路由
/**
*
* default异步函数解析之前设置数据默认值的工厂函数 - 特别适用于该`lazy: true`选项
*
****/
default?: () => DataT | Ref<DataT> | null,
transform?: (input: DataT) => DataT, // 可用于`handler`在解析后更改函数结果 的函数
pick?: string[], //只从函数结果 中选择这个数组中的指定键
watch?: WatchSource[], //自动刷新
initialCache?: boolean, //是否跳过有效负载缓存以进行初始提取,默认false
immediate?: boolean //阻止请求立即触发,默认true
}
interface RefreshOptions {
_initial?: boolean
}
type AsyncData<DataT, ErrorT> = {
data: Ref<DataT | null>, //传递参数
pending: Ref<boolean>, //是否仍在获取数据,booolean
execute: () => Promise<void>, //刷新`handler`函数 返回的数据 , fun
refresh: (opts?: RefreshOptions) => Promise<void>,//刷新`handler`函数 返回的数据, fun
error: Ref<ErrorT | null> //数据获取失败时的错误对象
}
//使用
const { data, pending, error, refresh } = await useAsyncData(
'mountains',
() => $fetch('https://api.nuxtjs.dev/mountains')
)
8、useCookie读写 cookie。例如:
const cookie = useCookie(name, options)
//使用
<template>
<div>
<h1> Counter: {{ counter || '-' }}</h1>
<button @click="counter = null">
reset
</button>
<button @click="counter--">
-
</button>
<button @click="counter++">
+
</button>
</div>
</template>
<script setup>
const counter = useCookie('counter')
//如果 cookie 不存在,则最初将其设置为随机值
counter.value = counter.value || Math.round(Math.random() * 1000)
</script>
9、 useError捕获全局错误。例如:
const error = useError()
10、useFetch 获取数据
这个可组合提供了一个方便的包装器useAsyncData和$fetch. 它会根据 URL 和 fetch 选项自动生成密钥,根据服务器路由为请求 url 提供类型提示,并推断 API 响应类型。
//类型
function useFetch(
url: string | Request | Ref<string | Request> | () => string | Request, //要获取的 URL
options?: UseFetchOptions<DataT> //可选参数
): Promise<AsyncData<DataT>>
type UseFetchOptions = {
key?: string, //唯一的key,
method?: string, //请求方法
params?: SearchParams, //查询参数
body?: RequestInit['body'] | Record<string, any>, //请求正文 - 自动字符串化(如果传递了一个对象)
headers?: { key: string, value: string }[], //请求标头
baseURL?: string, //请求的基本 URL
server?: boolean, //是否获取服务器上的数据
lazy?: boolean, ////默认false,获取数据之前是否阻塞路由
immediate?: boolean, //当设置为 时`false`,将阻止请求立即触发。默认true
/**
*
* default异步函数解析之前设置数据默认值的工厂函数 - 特别适用于该`lazy: true`选项
*
****/
default?: () => DataT,
transform?: (input: DataT) => DataT, //解析后 可用于改变函数结果的函数
pick?: string[], //仅从`handler`函数结果中选择此数组中的指定键
watch?: WatchSource[], //自动刷新
initialCache?: boolean //当设置为 时`false`,将跳过有效负载缓存以获取初始数据
}
type AsyncData<DataT> = {
data: Ref<DataT>, //传入参数
pending: Ref<boolean>, //布尔值,指示是否仍在获取数据
refresh: () => Promise<void>, //一个函数,可以用来刷新`handler`函数返回的数据
execute: () => Promise<void>, //一个函数,可以用来刷新`handler`函数返回的数据
error: Ref<Error | boolean> //一个函数,可以用来刷新`handler`函数返回的数据
}
//例如:
const { data, pending, error, refresh } = await useFetch('https://api.nuxtjs.dev/mountains',{
pick: ['title']
})
//使用拦截器
const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
onRequest({ request, options }) {
// 设置请求头
options.headers = options.headers || {}
options.headers.authorization = '...'
},
onRequestError({ request, options, error }) {
// 请求数据错误回调
},
onResponse({ request, response, options }) {
// 获取响应数据回调
return response._data
},
onResponseError({ request, response, options }) {
//返回响应数据数据错误回调
}
})
11、 useHead添加自定义各个页面的头部属性。具体如下:
//类型
useHead(meta: Computable<MetaObject>): void
interface MetaObject extends Record<string, any> {
charset?: string, //文档的字符编码。
viewport?: string, //配置视口
meta?: Array<Record<string, any>>, //**默认值**:`width=device-width, initial-scale=1`
link?: Array<Record<string, any>>, //<link>标签
style?: Array<Record<string, any>>, //<style>标签
script?: Array<Record<string, any>>, //<script>标签
noscript?: Array<Record<string, any>>, //<noscript>标签
titleTemplate?: string | ((title: string) => string), //配置动态模板以自定义单个页面上的页面标题
title?: string, //单个页面上设置静态页面标题
bodyAttrs?: Record<string, any>, //<body>
htmlAttrs?: Record<string, any> // <html>
}
//使用方法
//自定义元素
<script setup lang = 'ts'>
const title = ref('My App')
const description = ref('My amazing Nuxt app')
useHead({
title,
meta: [
{
name: 'description',
content: description
}
]
})
</script>
//动态标题
<script setup lang = 'ts'>
/****
*
*`titleTemplate`设置为带有`%s`占位符的字符串或设置为`function`。
* 注意:nuxt.config中只能设置静态属性
*
*****/
useHead({
// 字符串,
// where `%s` is replaced with the title
titleTemplate: '%s - Site Title',
// 函数
titleTemplate: (productCategory) => {
return productCategory
? `${productCategory} - Site Title`
: 'Site Title'
}
})
</script>
//添加外部CSS
<script setup lang = 'ts'>
useHead({
link: [
{
rel: 'preconnect',
href: 'https://fonts.googleapis.com'
},
{
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
crossorigin: ''
}
]
})
</script>
//添加第三方脚本
<script setup lang = 'ts'>
useHead({
script: [
{
src: 'https://third-party-script.com',
body: true
}
]
})
</script>
12、useNuxtApp获取上下文的方法,它在客户端和服务器端都可用。可以使用这些属性来扩展和自定义您的应用程序以及共享状态、数据和变量。例如:
<script setup lang = 'ts'>
const nuxtApp = useNuxtApp()
//方法
//1、通过 provide(name,value) 创建插件
const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', (name) => `Hello ${name}!`)
console.log(nuxtApp.$hello('name'))
//2、通过 `hook(name,cb)` 创建插件
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:start', () => {
//业务逻辑存放处
})
nuxtApp.hook('vue:error', (..._args) => {
console.log('vue:error')
//
如果是客户端
if (process.client) {
console.log(..._args)
}
})
})
//3、callhook(name,...args)使用任何现有钩子调用时返回结果
await nuxtApp.callHook('my-plugin:init')
//useNuxtApp包含一下属性和方法
/****
*
* 1、component()
* 2、directive()
* 3、use()
* 4、ssrContext:url,event,payload
* 5、payload
* 6、isHydrating
*
****/
</script>
13、useRequestHeaders来访问传入的请求标头。例如:
// 获取所有请求头
const headers = useRequestHeaders()
// 仅获取请求头 cookie
const headers = useRequestHeaders(['cookie'])
14、 useRouter获取路由器实例。例如:
const router = useRouter();
router.back();
router.forward();
router.go();
router.push({ path: "/home" });
router.replace({ hash: "#bio" });
15、useRuntimeConfig公共配置变量。使用如下:
<script setup lang="ts">
const config = useRuntimeConfig()
</script>
//服务端
export default defineEventHandler((event) => {
const config = useRuntimeConfig()
})
//定义运行时配置
export default defineNuxtConfig({
runtimeConfig: {
// 端口
apiSecret: '123',
// 公共请求地址
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api'
}
}
})
16、useState。详细如下:
// key:确保数据获取在请求之间正确重复数据删除的唯一键
// init:一个在未启动时为状态提供初始值的函数。这个函数也可以返回一个`Ref`
// T:指定状态的类型
useState<T>(init?: () => T | Ref<T>): Ref<T>
useState<T>(key: string, init?: () => T | Ref<T>): Ref<T>