Nuxt3快速入门

744 阅读14分钟

项目初始化

项目创建

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中 image.png

自定义布局

如果我们的布局文件名不叫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>

runtimeConfigapp.config

这两个的作用都是暴露变量给项目中使用。那在实际项目开发过程中到底使用哪个呢。 runtimeConfig: 项目中需要使用指定的私有和仅有的tokes时 app.config: 可以放一些需要在构建时使用的公共Token, 例如主题变量,标题等不敏感的数据

env配置

.env的变量会打入到process.env中,符合规则的会覆盖runtimeConfig的变量 默认加载 .env 文件的配置

截屏2023-05-19 下午9.03.09.png

.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 generatenuxi build --prerender 命令将应用部署到静态托管服务上,那么默认情况下,Nuxt 会将每个页面渲染为单独的静态 HTML 文件。

如果你只使用客户端渲染,这可能是没有必要的。你只需一个index.html 文件,再加上 200.html404.html 的回退页面,你可以告诉你的静态网页托管服务对所有请求提供这些文件。 为了实现这一点,我们可以改变路由的预渲染方式。只需在你的 nuxt.config.ts 文件中的 hooks 添加以下内容:

export default defineNuxtConfig({
  hooks: {
    'prerender:routes' ({ routes }) {
      routes.clear() // Do not generate any routes (except the defaults)
    }
  },
})

然后,Nuxt只生成三个文件

  • index.html
  • 200.html
  • 404.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提供两个可直接从中间件返回的方法

  1. navigateTo

    navigateTo (to: RouteLocationRaw | undefined | null, options?: { replace: boolean, redirectCode: number, external: boolean )
    

    此方法可以在中间件或插件中重定向到指定的路由。 也可以直接调用来完成页面的跳转。

  2. abortNavigation

    abortNavigation (err?: string | Error)
    

    这个就是直接终止跳转,并可以返回一些错误信息。

中间件可能的返回值说明:

  1. 无返回值:也就是说当前中间件不会阻塞路由跳转。
  2. return navigateTo('/') or return navigateTo({ path: '/' }), 重定向到指定的路径,如何是在服务端的话,会设置 redirect code 为302
  3. return navigateTo('/', { redirectCode: 301 }), 重定向到指定的路径,如果直服务端的话,会设置 redirect code 为301 表示这个重定向的永久的。
  4. return abortNavigation() 终止当前的跳转
  5. 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>