Skip to content

S14-02 SSR-Nuxt3

[TOC]

API

h3

  • defineEventHandler()
  • Request
  • getQuery()
  • getRequestURL()
  • getRouterParams()
  • isMethod()
  • readBody()
  • Response

概述

什么是 Nuxt

Nuxt 官方文档:https://nuxt.com.cn/docs/getting-started/installation

在了解 Nuxt 之前,我们先来了解一下创建一个现代应用程序,所需的技术:

  • 支持数据双向绑定和组件化( Nuxt 选择了 Vue.js )。
  • 处理客户端的导航( Nuxt 选择了 vue-router )。
  • 支持开发中热模块替换和生产环境代码打包( Nuxt 支持 webpack 5 和 Vite )。
  • 兼容旧版浏览器,支持最新的 JavaScript 语法转译( Nuxt 使用 esbuild )。
  • 应用程序支持开发环境服务器,也支持服务器端渲染 或 API 接口开发。
  • Nuxt 使用 h3 来实现部署可移植性(h3 是一个极小的高性能的 http 框架)
    • 如:支持在 Serverless、Workers 和 Node.js 环境中运行。
    • image-20240911151818836

Nuxt是一个直观的 Web 框架

  • 自 2016 年 10 月以来,Nuxt 专门负责集成上述所描述的事情 ,并提供前端和后端的功能。
  • Nuxt 框架可以用来快速构建一个 Vue.js 应用程序,如支持 CSR 、SSR、SSG 渲染模式的应用等。

Nuxt 发展史

Nuxt.js:

  • 诞生于 2016 年 10 月 25 号,由 Sebastien Chopin 创建,主要是基于 Vue2 、Webpack2 、Node 和 Express。
  • 在 2018 年 1 月 9 日, Sebastien Chopin 正式宣布,发布 Nuxt.js 1.0 版本。
    • 重要的变化是放弃了对 node < 8 的支持
  • 2018 年 9 月 21 日, Sebastien Chopin 正式宣布,发布 Nuxt.js 2.0 版本。
    • 开始使用 Webpack 4 及其技术栈, 其它的并没有做出重大更改。
  • 2021 年 8 月 12 日至今,Nuxt.js 最新的版本为:Nuxt.js 2.15.8

Nuxt3:

  • 经过 16 个月的工作,Nuxt 3 beta 于 2021 年 10 月 12 日发布,引入了基于 Vue3、Vite 和 Nitro( 服务引擎 ) 。
  • 六个月后,2022 年 4 月 20 日,Pooya Parsa 宣布 Nuxt3 的第一个候选版本,代号为“Mount Hope”。
  • 2022 年 11 月 16 号, Pooya Parsa 再次宣布 Nuxt3 发布为第一个正式稳定版本

Nuxt3 特点

  • Vue 技术栈

    • Nuxt3 是基于 Vue3 + Vue Router + Vite 等技术栈,全程 Vue3+Vite 开发体验(Fast)。
  • 自动导包

    • Nuxt 会自动导入辅助函数组合 APIVue API ,无需手动导入。
    • 基于规范的目录结构,Nuxt 还可以对自己的组件插件使用自动导入。
  • 约定式路由

    • Nuxt 路由基于 vue-router,在 pages 目录中创建的每个页面,都会根据目录结构和文件名来自动生成路由
  • 渲染模式

    • Nuxt 支持多种渲染模式(SSRCSRSSG 等)

      image-20240911151850083

  • 利于搜索引擎优化

    • 服务器端渲染模式,不但可以提高首屏渲染速度,还利于 SEO
  • 服务器引擎

    • 开发环境中,它使用 RollupNode.js

    • 生产环境中,使用 Nitro 将您的应用程序和服务器构建到一个通用.output目录中。

      Nitro服务引擎提供了跨平台部署的支持,包括 Node、Deno、Serverless、Workers 等平台上部署。

Nuxt.js VS Nuxt3

image-20240911151901743

基础

环境搭建

前置环境:

  • Node.js:v18.0.0 或更新版本,推荐使用偶数版本。
  • 文本编辑器:我们推荐使用 Visual Studio Code 以及 Volar(Vue - Official) 扩展
  • 终端:用于运行 Nuxt 命令

新建项目:

1、在命令行中,运行以下命令:

sh
# pnpm
pnpm dlx nuxi init <project-name>

# npm
npx nuxi init <project-name>

2、进入到<project-name>目录,安装依赖。

sh
pnpm install

# 以前需要携带以下参数,用来创建一个扁平的node_modules目录结构
# pnpm install --shamefully-hoist

错误: 执行npx nuxi init 01-hello-nuxt 报如下错误:

image-20241007115137701

image-20240925181216112

分析:主要是网络不通导致。

解决:配置 host 翻墙

1、ping raw.githubusercontent.com 检查是否通。如果访问不通,代表是网络不通。

2、配置 host,本地解析域名:

  • Mac 电脑 host 配置路径: /etc/hosts
  • Win 电脑 host 配置路径:c:/Windows/System32/drivers/etc/hosts

3、在 host 文件中新增一行 ,编写如下配置:

sh
185.199.108.133 raw.githubusercontent.com

# 或者以下其中一个
185.199.108.133 raw.githubusercontent.com
185.199.109.133 raw.githubusercontent.com
185.199.110.133 raw.githubusercontent.com
185.199.111.133 raw.githubusercontent.com

4、重新 ping 域名,如果通了就可以用了。

5、重新开一个终端创建项目即可。

目录结构

image-20240911152025639

package.json 脚本:

image-20240925220356405

注意: postinstall 是一种 npm 生命周期脚本,在安装依赖包后自动执行。它可以用于执行一些初始化任务,比如构建项目、复制文件或设置环境变量。

json
"scripts": {
  "postinstall": "node setup.js"
}

app.vue

默认情况下,Nuxt 会将此文件视为入口,并为应用程序的每个路由呈现其内容。

作用:

  • 布局:定义页面布局 Layout 或 自定义布局,如:<NuxtLayout>
  • 路由占位:定义路由的占位,如:<NuxtPage>
  • 编写全局样式
  • 全局监听路由

image-20240911152103373

配置

nuxt.config▸

nuxt.config.ts 配置文件位于项目的根目录,可对 Nuxt 进行自定义配置。

Nuxt 配置:

  • runtimeConfig{ public, 自定义 }运行时配置,即定义环境变量。支持服务器和客户端的变量。

    • 注意: 配置会被.env配置覆盖。

    • public{ 自定义 },这个部分的配置会被公开到客户端,因此可以在前端服务端代码中都可以安全使用。

      • 自定义:``,自定义的环境变量。
    • ...自定义:``,自定义的私有环境变量。这个部分的配置仅在服务器端可用,不能被客户端访问。

      js
      // 1. 在runtimeConfig 中定义环境变量
      runtimeConfig: {
        public: {
          apiUrl: process.env.API_URL,
          xxx: 'http://baidu.com'
        },
        yyy: 'yyy',
        zzz: 'zzz'
      },
      js
      // 2. 访问环境变量:useRuntimeConfig()
      const config = useRuntimeConfig()
      
      // 在客户端
      if (process.client) {
        const apiUrl = config.public.apiUrl
      }
      
      // 在服务器端
      if (process.server) {
        const apiUrl = config.public.apiUrl
        const secret = config.yyy
      }
  • appConfig{ 自定义 },应用配置,定义在构建时确定的公共变量,如:theme。

    • 注意: 配置会和app.config.ts配置合并。优先级: app.config.ts > appConfig

    • 注意: 前后端都可以访问配置项。

      js
      export default defineNuxtConfig({
        appConfig: {
          siteName: 'My Awesome Site',
          apiBase: process.env.API_BASE_URL || 'https://api.example.com'
        }
      })
      js
      // 访问配置:useAppConfig()
      const appConfig = useAppConfig()
      
      console.log(appConfig.siteName) // 输出: My Awesome Site
      console.log(appConfig.apiBase) // 输出: https://api.example.com
  • app{baseURL, head},应用相关的配置,如头部信息、全局样式等。

    • baseURLstring,定义应用的基础 URL,通常用于处理相对路径。

    • buildAssetsDirstring,用于自定义构建生成的静态资产的目录。

    • cdnURLstring,用于指定 CDN(内容分发网络)的基本 URL,这样你可以将静态资源托管在 CDN 上,提高加载速度。

    • head{title, meta, link, style, script},给每个页面上设置 head 信息,也支持 useHead 配置和内置组件。

      • titlestring,标题。

      • meta[{charset},{ name, content}],元数据。

        • {charset}:``,字符编码。
        • { name, content}:``,
      • link[{rel, href}],外链样式。

      • style[{children, type}],样式。

      • script[{src, body?}],JS 脚本。

        • src:``,script 连接地址。
        • body?boolean是否将 script 添加到 body 标签中。
      js
        app: {
          baseURL: '/my-app/',
          buildAssetsDir: 'static/assets/', // 自定义静态资源目录
          cdnURL: 'https://cdn.example.com/', // 替换为你的CDN地址
          head: {
            meta: [
              // <meta name="viewport" content="width=device-width, initial-scale=1">
              { name: 'viewport', content: 'width=device-width, initial-scale=1' }
            ],
          script: [
            // <script src="https://myawesome-lib.js"></script>
            { src: 'https://awesome-lib.js' }
          ],
          link: [
            // <link rel="stylesheet" href="https://myawesome-lib.css">
            { rel: 'stylesheet', href: 'https://awesome-lib.css' }
          ],
          // please note that this is an area that is likely to change
          style: [
            // <style type="text/css">:root { color: red }</style>
            { children: ':root { color: red }', type: 'text/css' }
          ],
          noscript: [
            // <noscript>JavaScript is required</noscript>
            { children: 'JavaScript is required' }
          ]
        }
      }
  • ssrboolean默认:true,指定应用的渲染模式。

    • true:开启 SSR 渲染。
    • false:关闭 SSR 渲染,此时属于 SPA 应用。
  • router{base, middleware, options},配置路由相关的信息,比如在客户端渲染可以配置 hash 路由。

    • basestring,用于设置应用的基础路径。如果你的应用部署在一个子目录下,可以通过这个选项来指定。

    • middlewarestring[],指定全局中间件,可在每个页面的渲染之前执行一些逻辑,如身份验证等。

    • options{hashMode}

      • hashModeboolean,是否使用 hash 路由(只在 SPA 应用中有效)。开启 hash 路由
      js
      router: {
        base: '/app/',
        middleware: ['auth'],
        options: {
            hashMode: true // hash路由
        }
      },
      js
      // 开启hash路由
      export default defineNuxtConfig({
        ssr: false,
        router: {
          options: {
            hashMode: true
          }
        }
      })
  • cssstring[],全局 CSS 文件。

    js
    css: ['@/assets/styles/global.css'],
  • modulesstring[],引入 Nuxt 模块,增强功能。

    js
    modules: ['@nuxtjs/axios', '@nuxtjs/pwa'],
  • plugins[{src, mode}],定义插件,支持客户端和服务端。

    • src:``,

    • mode:``,

      js
      plugins: [{ src: '~/plugins/myPlugin.js', mode: 'client' }],
  • routeRules:``,定义路由规则,可更改路由的渲染模式或分配基于路由缓存策略(公测阶段)

    • :``,
  • middlewarestring[],定义全局中间件。

    js
    router: {
      middleware: ['auth'],
    },
  • alias:``,路径的别名,默认已配好。

    • :``,
  • build{extractCSS, extend(config, { isDev })},Webpack 构建相关配置。

    • extractCSS:``,

    • extend(config, { isDev }):``,

      js
      build: {
        extractCSS: true,
        extend(config, { isDev }) {
          if (isDev) {
            config.devtool = 'source-map';
          }
        },
      },
  • builder:``,可指定用 vite 还是 webpack 来构建应用,默认是 vite。如切换为 webpack 还需要安装额外的依赖。

    • :``,
  • i18n{locales, defaultLocale},配置国际化支持。

    • locales[{code, name}]

      • code:``,
      • name:``,
    • defaultLocalestring

      js
      i18n: {
        locales: [
          { code: 'en', name: 'English' },
          { code: 'fr', name: 'Français' },
        ],
        defaultLocale: 'en',
      },
runtimeConfig
  • runtimeConfig{ public, 自定义 }运行时配置,即定义环境变量。支持服务器和客户端的变量。

    • 注意: 配置会被.env配置覆盖。

    • public{ 自定义 },这个部分的配置会被公开到客户端,因此可以在前端服务端代码中都可以安全使用。

      • 自定义:``,自定义的环境变量。
    • ...自定义:``,自定义的私有环境变量。这个部分的配置仅在服务器端可用,不能被客户端访问。

      js
      // 1. 在runtimeConfig 中定义环境变量
      runtimeConfig: {
        public: {
          apiUrl: process.env.API_URL,
          xxx: 'http://baidu.com'
        },
        yyy: 'yyy',
        zzz: 'zzz'
      },
      js
      // 2. 访问环境变量:useRuntimeConfig()
      const config = useRuntimeConfig()
      
      // 在客户端
      if (process.client) {
        const apiUrl = config.public.apiUrl
      }
      
      // 在服务器端
      if (process.server) {
        const apiUrl = config.public.apiUrl
        const secret = config.yyy
      }

定义环境变量的方式:

方式一:在nuxt.config.ts中配置runtimeConfig

image-20240911152127865

访问环境变量:

image-20240911152140048

方式二:在.env中配置环境变量

可以通过.env 文件中的环境变量来覆盖runtimeConfig中的配置,优先级:.env > runtimeConfig

  • .env 的变量会添加到 process.env 中,符合规则的会覆盖 runtimeConfig 的变量。
  • .env 一般用于某些终端启动应用时动态指定配置,不区分 dev 和 pro 环境。

注意:

  • 注意常量写法和驼峰写法之间的对应关系:

    • NUXT_APP_KEY - appKey

    • NUXT_PUBLIC_BASE_URL - public.baseURL

    • image-20240926101647264

  • .env中定义的环境变量必须在 nuxt.config 中定义。这样可以确保任意的环境变量不会暴露给应用程序代码。

获取.env 文件中的环境变量:

和方式一一样,也是通过 useRuntimeConfig() 获取环境变量。

注意: 无法通过 process.env 获取。可能需要 dotenv 插件。

appConfig
  • appConfig{ 自定义 },应用配置,定义在构建时确定的公共变量,如:theme。

    • 注意: 配置会和app.config.ts配置合并。优先级: app.config.ts > appConfig

    • 注意: 前后端都可以访问配置项。

      js
      export default defineNuxtConfig({
        appConfig: {
          siteName: 'My Awesome Site',
          apiBase: process.env.API_BASE_URL || 'https://api.example.com'
        }
      })
      js
      // 访问配置:useAppConfig()
      const appConfig = useAppConfig()
      
      console.log(appConfig.siteName) // 输出: My Awesome Site
      console.log(appConfig.apiBase) // 输出: https://api.example.com

抽取配置到app.config.ts

创建app.config.ts文件,并配置如下:

image-20240926110113546

app
  • app{baseURL, head},应用相关的配置,如头部信息、全局样式等。

    • baseURLstring,定义应用的基础 URL,通常用于处理相对路径。

    • buildAssetsDirstring,用于自定义构建生成的静态资源的目录。

    • cdnURLstring,用于指定 CDN(内容分发网络)的基本 URL,这样你可以将静态资源托管在 CDN 上,提高加载速度。

    • head{title, meta, link, style, script},给每个页面上设置 head 信息,也支持 useHead 配置和内置组件。

      • titlestring,标题。

      • meta[{charset},{ name, content}],元数据。

        • {charset}:``,字符编码。
        • { name, content}:``,
      • link[{rel, href}],外链样式。

      • style[{children, type}],样式。

      • script[{src, body?}],JS 脚本。

        • src:``,script 连接地址。
        • body?boolean是否将 script 添加到 body 标签中。
      js
        app: {
          baseURL: '/my-app/',
          buildAssetsDir: 'static/assets/', // 自定义静态资源目录
          cdnURL: 'https://cdn.example.com/', // 替换为你的CDN地址
          head: {
            meta: [
              // <meta name="viewport" content="width=device-width, initial-scale=1">
              { name: 'viewport', content: 'width=device-width, initial-scale=1' }
            ],
          script: [
            // <script src="https://myawesome-lib.js"></script>
            { src: 'https://awesome-lib.js' }
          ],
          link: [
            // <link rel="stylesheet" href="https://myawesome-lib.css">
            { rel: 'stylesheet', href: 'https://awesome-lib.css' }
          ],
          // please note that this is an area that is likely to change
          style: [
            // <style type="text/css">:root { color: red }</style>
            { children: ':root { color: red }', type: 'text/css' }
          ],
          noscript: [
            // <noscript>JavaScript is required</noscript>
            { children: 'JavaScript is required' }
          ]
        }
      }

应用:

  • 可以在 app 选项中做 SEO 优化配置。

添加 app 配置的方式:

优先级内置组件 > useHead() > nuxt.config.ts

方式一: 使用useHead()钩子函数动态添加 app 配置项。

  • 如果在app.vue中使用useHead()会在项目全局起作用
  • 如果在某个vue文件中使用,则只会在该页面起作用

image-20240926153506061

方式二: 使用<Head><Meta><Style><Link><Script>这些内置组件添加 app 配置项。

image-20240926154325462

方式三:nuxt.config.ts配置的app配置项中设置。

app.config

app.config.ts用于公开在构建时确定的公共变量

注意:

  • app.config.ts 配置文件中的选项不能使用 env 环境变量来覆盖,与 runtimeConfig 不同。
  • 不要存放秘密或敏感信息在 app.config.ts 文件中,该文件是客户端公开

配置: 使用辅助函数 defineAppConfig() 提供代码提示

js
export default defineAppConfig({
  title: 'Hello Nuxt',
  theme: {
    dark: true,
    colors: {
      primary: '#ff0000'
    }
  }
})

访问配置:

js
// pages/index.vue
const appConfig = useAppConfig()

console.log(appConfig.theme.dark)

应用场景:

  • 全局常量:用于存放网站名称、主题色等不变的全局信息。
  • API 配置:方便管理 API 的基础 URL,便于调用。
  • 主题设置:可以集中管理主题相关的配置,比如颜色、字体等。
  • 功能开关:可以用于管理不同环境下的功能开关。

runtimeConfig 与 app.config

runtimeConfig 和 app.config 都用于向应用程序公开变量。要确定是否应该使用其中一种,以下是一些指导原则:

  • runtimeConfig:定义环境变量,比如:运行时需要指定的私有或公共 token。
  • app.config:定义公共变量,比如:在构建时确定的公共 token、网站配置。

image-20240911152223144

内置组件

Nuxt3 框架也提供一些内置的组件,常用的如下:

  • SEO 组件: Html、Body、Head、Title、Meta、Style、Link、NoScript、Base
  • NuxtWelcome:欢迎页面组件,该组件是 @nuxt/ui 的一部分
  • NuxtLayout:是 Nuxt 自带的页面布局组件
  • NuxtPage:是 Nuxt 自带的页面占位组件
    • 需要显示位于目录中的顶级或嵌套页面 pages/
    • 是对 router-view 的封装
  • ClientOnly:该组件中的默认插槽的内容只在客户端渲染。而 fallback 插槽的内容只在服务器端渲染。
  • NuxtLink:是 Nuxt 自带的页面导航组件
    • 是 Vue Router<RouterLink>组件 和 HTML<a>标签的封装。
  • 等等

NuxtPage

NuxtPage:是 Nuxt 自带的页面占位组件<NuxtPage> 是对 Vue Router 的 <RouterView> 组件的封装。

1、在app.vue中使用占位组件<NuxtPage>

image-20240926163205783

2、在/pages/目录下创建组件home.vueindex.vue

3、直接输入路由地址:localhost:3000访问index.vuelocalhost:3000/home访问home.vue

image-20241007145501170

ClientOnly

ClientOnly:该组件中的默认插槽的内容只在客户端渲染。而 fallback 插槽的内容只在服务器端渲染

加载过程中显示 fallback 渲染的内容,等加载完毕就会用客户端渲染的内容替换 fallback 渲染的内容。

  • ClientOnly(),只在客户端渲染其插槽内容。
    • 属性
    • fallback:``,渲染占位
    • fallback-tag:``,渲染占位标签
    • 插槽
    • fallback:``,指定在服务器端显示的内容。

示例:

html
<ClientOnly fallback-tag="span" fallback="加载评论中...">
  <!-- 这将在客户端渲染 -->

  <template #fallback>
    <!-- 这将在服务器端渲染 -->
    <p>加载评论中...</p>
  </template>
</ClientOnly>

NuxtLink(),用来实现页面的导航。

  • 属性:
  • tostring,指定要导航的目标路径。支持路由路径路由对象URL
  • hrefstring,to 的别名。
  • active-class默认:nuxt-link-active,激活链接的类名。
  • target_blank | _self |_parent |_top,与普通 <a> 标签相同,指定链接打开方式。

全局样式

手动导入

依赖包:

  • sass
    • 安装:pnpm i sass -D

基本使用:

1、在.css.scss文件中编写样式

image-20241007152247143

2、在nuxt.config.tscss中配置全局样式

image-20240926172444853

3、此时可以在组件中直接使用全局样式。

使用变量或混入:

1、在.scss文件中编写变量或混入

image-20240926171347604

2、在全局或局部组件中引入.scss文件并使用变量或混入

方式一:使用@import引入

  • 全局组件

    image-20240926171631622

  • 局部组件

    image-20240926171506474

方式二:使用@use引入(推荐

image-20240926171936036

自动导入▸

1、在.css.scss文件中编写样式

2、在nuxt.config.tsvite中配置全局样式

注意: 此处导入的文件和css配置项中导入的文件不能重复。

js
export default defineNuxtConfig({
  css: ['~/assets/css/main.css'],

  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          // 在每个 SCSS 文件的开头自动引入多个scss文件。
          additionalData: `
            @use "@/assets/styles/global.scss" as *;
            @use "@/assets/styles/variables.scss" as *;
            @use "@/assets/styles/mixins.scss" as *;
          `
        }
      }
    }
  }
})

3、此时可以在组件中直接使用全局样式。

资源导入

public 目录

public 目录用作应用程序的公共服务器,用于存放在应用程序的指定 URL 下公开访问的静态资源。

public/img/ 目录中引用一个图像文件,该文件可通过静态 URL /img/nuxt.png 访问:

html
<template>
  <img src="/img/nuxt.png" alt="Discover Nuxt 3" />
</template>

静态的 URL /img/nuxt.png 也支持在背景中使用:

html
<template>
  <div class="box"></div>
</template>
<style lang="scss">
  .box {
    width: 200px;
    height: 150px;
    background-image: url(/img/nuxt.png);
  }
</style>

assets 目录

assets 目录经常用于存放如样式表字体SVG 的资源

可以使用 ~/assets/ 路径引用位于 assets 目录中的资产文件

html
<template>
  <img src="~/assets/img/nuxt.png" alt="Discover Nuxt 3" />
</template>

~/assets/ 路径也支持在背景中使用

html
<template>
  <div class="box"></div>
</template>
<style lang="scss">
  .box {
    width: 200px;
    height: 150px;
    background-image: url(~/assets/img/nuxt.png);
  }
</style>

注意: 同样支持@/assets/base64的写法。

html
<img src="@/assets/img/nuxt.png" alt="Discover Nuxt 3" />

<img src="base64;type=image/png xxx" alt="Discover Nuxt 3" />

字体图标

1、将字体图标存放在 assets 目录下

image-20240926180407911

2、字体文件可以使用 ~/assets/ 路径引用。

image-20240926180425116

3、在 nuxt.config 配置文件中导入全局样式

image-20240926180526079

4、在页面中就可以使用字体图标了

image-20240926180656587

路由

新建页面

Nuxt 项目中的页面是在 pages 目录下创建的。

pages 目录创建的页面,Nuxt 会根据该页面的目录结构和其文件名来自动生成对应的路由

页面路由也称为文件系统路由器(file system router),路由是 Nuxt 的核心功能之一。

手动创建页面:

  • 1.创建页面文件,比如: pages/index.vue
  • 2.将<NuxtPage />内置组件添加到 app.vue
  • 3.页面如果使用 scss 那么需要安装:npm i sass -D

image-20240911152411388

命令行创建页面:

语法:

sh
pnpm dlx nuxi add page <path>

说明:

  • pnpm dlx nuxi:相当于npx nuxi
  • add page:创建页面的命令
  • <path>:要添加的页面路径,可以是相对路径。有以下写法:
    • about:创建pages/about.vue页面。
    • about/index:创建pages/about/index.vue页面。
    • about/[id]:创建pages/about/[id].vue页面。
    • about-[role]/[id]:创建pages/about-[role]/[id].vue页面。

示例:

sh
npx nuxi add page home # 创建 home 页面
npx nuxi add page detail/[id] # 创建 detail 页面
npx nuxi add page user-[role]/[id] # 创建 user 页面

image-20240911152419264

导航

组件导航

NuxtLink(),用来实现页面的导航。

  • 属性:

  • tostring | {path, query | params},指定要导航的目标路径。支持路由路径路由对象URL

  • hrefstring,to 的别名。

  • active-class默认:nuxt-link-active,激活链接的类名。

  • target_blank | _self |_parent |_top,与普通 <a> 标签相同,指定链接打开方式。

  • replaceboolean,如果设置为 true,则在导航时使用替换模式,这意味着不会在历史记录中添加新条目。

  • prefetchboolean,如果设置为 true,为进入视口的链接启用预加载资源。

    html
    <template>
      <nav>
        <NuxtLink to="/home">Home</NuxtLink>
        <NuxtLink :to="{path: '/home', query: {id: 10}}">Home</NuxtLink>
        <NuxtLink to="https://www.baidu.com">Home</NuxtLink>
    
        <NuxtLink href="/home">Home</NuxtLink>
    
        <NuxtLink to="/about" active-class="active">About</NuxtLink>
    
        <NuxtLink to="/profile" target="_blank">Profile</NuxtLink>
    
        <NuxtLink to="/profile" replace>Profile</NuxtLink>
    
        <NuxtLink to="/about" prefetch>About</NuxtLink>
    
        <!-- 使用a标签页面会刷新 -->
        <a href="/more">More</a>
      </nav>
    </template>

<NuxtLink> 是 Nuxt 内置组件,是对 <RouterLink> 的封装,用来实现页面的导航

<NuxtLink>在客户端视口中可见时,Nuxt 会自动预取链接页面的组件和负载(生成的页面),从而加快导航速度。

该组件底层是一个<a>标签,因此使用 a + href 属性也支持路由导航。但是用 a 标签导航会有触发浏览器默认刷新事件,而 NuxtLink 不会,NuxtLink 还扩展了其它的属性和功能。

应用 Hydration 后(已激活,可交互),页面导航会通过前端路由来实现。这可以防止整页刷新

当然,手动输入 URL 后,点击刷新浏览器也可导航,这会导致整个页面刷新。

image-20240911152433425

编程导航

  • navigateTo()(to , options?),用于进行编程导航的函数。非常适合需要在事件处理程序生命周期钩子中进行导航的场景。

    • tostring | {path, query | params},目标路由的路径,可以是一个字符串(如 /about),也可以是一个对象,包含更复杂的路由信息。

    • options?{replace, external},用于控制导航的行为。

      • replaceboolean默认:false,为 true 时会替换当前路由页面。
      • externalboolean默认:false,为 true 时允许导航到外部连接。
    • 返回:

    • promisePromise

      js
      // to:字符串路径
      navigateTo('/about')
      
      // to:对象路径-query
      navigateTo({
        path: '/home',
        query: { name: 'tom' }
      })
      
      // to:对象路径-params
      navigateTo({
        path: '/posts/[id]',
        params: { id: postId }
      })
      
      // repalce选项:替换当前历史记录条目
      navigateTo('/home', { replace: true })
      
      // external选项:跳转到外部链接(必须设置external:true)
      navigateTo('https://www.baidu.com', { external: true })
  • useRouter()(),用于访问 Vue Router 实例。可以在组件中实现导航功能

    • routerRouter,提供了一系列方法和属性,以便于进行路由导航和获取当前路由信息。

      js
      import { useRouter } from 'vue-router'
      
      const router = useRouter()
  • router.push()(to),导航到一个新的路由。建议用 navigateTo ,支持性更好。

    • tostring | {path, query | params}

      js
      router.push('/about')
  • router.replace()(to),替换当前的路由,而不是添加新条目。建议用 navigateTo ,支持性更好。

    • tostring | {path, query | params}

      js
      router.replace('/home')
  • router.go()(count),前进或后退指定数量的历史记录条目。

    • countnumber | -number

      js
      router.go(-1) // 后退一步
  • router.back()(),回到上一个路由。

    • :``,

      js
      router.back()
  • router.forward()(),页面前进。

    • :``,

      js
      router.forward()
  • router.beforeEach()((to, from, next) => void),注册全局前置守卫(通常在应用初始化时使用)。

    • to:``,

    • from:``,

    • next:``,

      js
      router.beforeEach((to, from, next) => {
        // 逻辑
        next()
      })
  • router.afterEach()((to, from, next) => void),注册全局后置守卫。

    • to:``,

    • from:``,

    • next:``,

      js
      router.afterEach((to, from, next) => {
        // 逻辑
        next()
      })

navigateTo():

Nuxt3 除了可以通过<NuxtLink>内置组件来实现导航,同时也支持编程导航:navigateTo()

通过编程导航,在应用程序中就可以轻松实现动态导航了,但是编程导航不利于 SEO

navigateTo 函数在服务器端客户端可用,也可以在插件、中间件中使用,也可以直接调用以执行页面导航,例如:

  • 当用户触发该 goToProfile()方法时,我们通过 navigateTo 函数来实现动态导航。
  • 建议: goToProfile 方法总是返回 navigateTo 函数(该函数不需要导入)或 返回异步函数

image-20240911152444836

useRouter():

Nuxt3 中的编程导航除了可以通过 navigateTo 来实现导航,同时也支持 useRouter() ( 或 Options API 的 this.$router )

image-20240911152500451

动态路由

Nuxt3 和 Vue 一样,也是支持动态路由的,只不过在 Nuxt3 中,动态路由也是根据目录结构和文件的名称自动生成。

语法:

动态路由在 Nuxt 3 中通过在 pages 目录中使用方括号([])来定义。方括号里编写动态路由的参数。

sh
# /detail/:id
pages/detail/[id].vue

# /detail/user-:id
pages/detail/user-[id].vue

# /detail/:role/:id
pages/detail/[role]/[id].vue

# /detail-:role/:id
pages/detail-[role]/[id].vue

路由参数

动态路由参数: route.params

1、通过 [] 方括号语法定义动态路由,比如:/detail/[id].vue

2、页面跳转时,在 URL 路径中传递动态路由参数,比如:/detail/10010

3、目标页面通过 route.params 获取动态路由参数

js
import { useRoute } from 'vue-router'

const route = useRoute()
const userId = route.params.id // 获取动态路由参数

查询字符串参数: route.query

1、页面跳转时,通过查询字符串方式传递参数,比如:/detail/10010?name=liujun

2、目标页面通过 route.query 获取查询字符串参数

js
import { useRoute } from 'vue-router'

const route = useRoute()
const name = route.query.name // 获取查询字符串参数

全匹配路由

如果你需要一个全匹配路由,你可以创建一个名为 [...slug].vue 的文件。这将匹配该路径下的所有路由。

获取路由参数: 可以通过 $route.params 获取路由参数。

html
<!-- pages/[...slug].vue -->

<template>
  <p>{{ $route.params.slug }}</p>
</template>

导航到 /hello/world 将渲染:

html
<p>["hello", "world"]</p>

注意:

  • [...slug].vue 文件除了支持在 pages 根目录下创建,也支持在其子目录中创建。
  • Nuxt3 正式版不支持 404.vue 页面了,以前的候选版是支持的 404.vue,但是 Next.js 是支持。

应用: 匹配 404 页面

捕获所有不配路由(即 404 页面)

方式一: [...404].vue:在根目录或其子目录中添加[...404].vue页面。

image-20241008144739725

方式二: error.vue:在根目录中添加error.vue页面。

html
<script setup lang="ts">
  const props = defineProps({
    error: Object
  })

  const handleError = () => clearError({ redirect: '/' })
</script>

<template>
  <div>
    <h2>{{ error.statusCode }}</h2>
    <button @click="handleError">清除错误</button>
  </div>
</template>

error 对象提供以下字段:

js
{
  url: string
  statusCode: number
  statusMessage: string
  message: string
  description: string
  data: any
}

路由匹配规则

优先级: 预定义路由 > 动态路由 > 全匹配路由

  • 预定义路由pages/detail/create.vue
    • 匹配 /detail/create
  • 动态路由pages/detail/[id].vue
    • 匹配/detail/1, /detail/abc
    • 不匹配 /detail/create/detail/1/1/detail/
  • 全匹配路由pages/detail/[...slug].vue
    • 匹配 /detail/1/2, /detail/a/b/c
    • 不匹配 /detail

嵌套路由

Nuxt 和 Vue 一样,也是支持嵌套路由的,只不过在 Nuxt 中,嵌套路由也是根据目录结构和文件的名称自动生成

嵌套路由步骤:

1、~pages/parent.vue:创建一个一级路由。

  • ~pages/parent.vue 中添加 <NuxtPage/>路由占位。

2、~pages/parent/:创建一个与一级路由同名同级的文件夹。

3、在 parent 文件夹下,创建一个嵌套的二级路由。

  • ~pages/parent/child.vue:二级路由页面。

  • ~pages/parent/index.vue :二级路由默认页面。

目录结构:

sh
-| pages/
---| parent.vue
---| parent/
------| child.vue

路由结构:

image-20240911152614587

路由中间件

  • definePageMeta()({middleware}),是一个编译器宏,你可以用它为位于pages/目录中的页面组件设置元数据。
    • middleware[],直接定义匿名或命名中间件。
    • :``,
    • 返回:
    • :``,

文档:https://nuxt.com.cn/docs/guide/directory-structure/middleware

Nuxt 提供了一个可定制的 路由中间件用来监听路由的导航。包括局部全局监听。支持服务端客户端

路由中间件分为三种:

  • 匿名(内联)路由中间件:在页面中使用 definePageMeta() 函数定义,可监听局部路由。当注册多个中间件时,会按照注册顺序来执行。
  • 命名路由中间件:在 middleware 目录下定义,并会自动加载中间件。命名规范 kebab-case。
  • 全局路由中间件:在 middleware 目录中,需带 .global 后缀的文件,每次路由更改都会自动运行。优先级比前面的高,支持两端。

匿名(内联)路由中间件:

在页面中使用 definePageMeta() 函数定义,可监听局部路由。当注册多个中间件时,会按照注册顺序来执行。

js
definePageMeta({
  // 将中间件定义为函数
  middleware: [
    function (to, from) { // 没有next参数
      const auth = useState('auth')

      if (!auth.value.authenticated) {
          // return返回 null、''或没有return时,会继续执行下一个中间件
          // return返回 navigateTo()路由跳转时,直接跳转到指定页面。不会执行下一个中间件
          return navigateTo('/login')
      }

      if (to.path !== '/checkout') {
        return navigateTo('/checkout')
      }
    }
  ],

  // ... 或者设置为字符串
  middleware: 'auth'

  // ... 或者设置为多个字符串
  middleware: ['auth', 'another-named-middleware']
})

命名路由中间件:

在 middleware 目录下定义,并会自动加载中间件。命名规范 kebab-case。

1、在~/middleware目录中通过 defineNuxtRouterMiddleware() 定义命名的中间件home.ts

注意: 执行时机:

  • server:刷新浏览器时会在服务端执行中间件。
  • client:切换路由时,只会在客户端执行。

image-20241008170913189

2、在某个页面中通过 definePageMeta({middleware}) 使用定义的命名中间件。

image-20241008170711439

全局路由中间件:

在 middleware 目录中,需带 .global 后缀的文件,每次路由更改都会自动运行。优先级比前面的高,支持两端。

1、在~/middleware目录中通过 defineNuxtRouterMiddleware() 定义命名的中间件auth.global.ts

image-20241008172555740

2、此时该中间件已经在全局适用。每次路由跳转时都会执行一次该中间件。

路由验证

Nuxt 支持对每个页面路由进行验证,我们可以通过 definePageMeta() 中的 validate 属性来对路由进行验证。

  • validate 属性接受一个回调函数,回调函数中以 route 作为参数
  • 回调函数的返回值支持:
    • 返回 bool 值来确定是否放行路由 ➢ true 放行路由 ➢ false 默认重定向到内置的 404 页面
    • 返回对象 ➢ { statusCode:401 } // 返回自定义的 401 页面,验证失败

1、使用definePageMeta() 中的 validate 属性验证路由是否符合规则

image-20241008173341768

2、如果验证失败,返回错误码statusCodestatusMessage

注意: statusMessage 不支持中文字符串。

image-20241008173932819

3、在项目根目录(不是 pages 目录)新建 error.vue文件,处理错误。

image-20241008173958787

4、清除错误信息:在error.vue页面通过 clearError() 方法清除错误信息并跳转到指定页面。

image-20240911152656539

Layout

Layout 布局是页面的包装器,可以将多个页面共性东西抽取到 Layout 布局 中,如页眉和页脚。

布局是使用 <slot /> 组件来显示页面内容的 Vue 文件。

Layout 布局有两种使用方式:

方式一:默认布局

  • layouts 目录下新建默认的布局组件,比如:layouts/default.vue。名字必须是default.vue

    image-20241008180202487

  • 然后在 app.vue 中通过<NuxtLayout>内置组件来使用。

    image-20241008180219980

    image-20241008180225587

方式二:自定义布局

  • layouts 目录下新建 Layout 布局组件,比如: layouts/custom-layout.vue。名字自定义。

    image-20241008181130379

  • app.vue 中给<NuxtLayout>内置组件不要指定 name 属性

  • image-20241008181457881

  • login.vue页面中使用 definePageMeta() 宏函数来指定 layout 布局。

    image-20241008181519050

渲染模式

浏览器 和 服务器都可以解释 JavaScript 代码,都可以将 Vue.js 组件呈现为 HTML 元素。此过程称为渲染

  • 客户端渲染:在客户端将 Vue.js 组件呈现为 HTML 元素。
  • 服务器渲染:在服务器将 Vue.js 组件呈现为 HTML 元素。

Nuxt3 支持多种渲染模式:

  • 客户端渲染(CSR): 只需将 ssr 设置为 false

    js
    export default defineNuxtConfig({
      ssr: false
    })
  • 服务器端渲染(SSR):只需将 ssr 设置为 true

    js
    export default defineNuxtConfig({
      ssr: true
    })
  • 混合渲染(SSR | CSR | SSG | SWR):需在 routeRules 根据每个路由动态配置渲染模式(beta 版本)

    js
    export default defineNuxtConfig({
      routeRules: {
        // 主页在构建时预渲染
        '/': { prerender: true },
    
        // 产品静态页面按需生成,后台自动重新验证
        '/products/**': { swr: 3600 },
    
        // 博客文章按需生成,直到下一次部署前持续有效
        '/blog/**': { isr: true },
    
        // 管理仪表板仅在客户端渲染
        '/admin/**': { ssr: false },
    
        // 在API路由上添加cors头
        '/api/**': { cors: true },
    
        // 跳转旧的URL
        '/old-page': { redirect: '/new-page' }
      }
    })

插件

API:

  • useNuxtApp()(),提供了一种访问 Nuxt 共享运行时上下文的方式,该上下文在客户端和服务器端都可用。它帮助你访问 Vue 应用程序实例、运行时钩子、运行时配置变量和内部状态,如ssrContextpayload

    • 返回:
    • nuxtApp{nuxtApp, $config, $pinia,自定义},返回一个包含 Nuxt 应用实例的对象。
      • nuxtApp:``,表示 Nuxt 应用的实例。
      • $config:``,应用的运行时配置。
      • $pinia:``,Pinia 实例,用于状态管理。
      • 自定义:``,自定义的提供的属性和方法,如通过插件提供的 API。
  • 属性

  • nuxtApp.vueApp(),用于访问 Vue 应用实例的属性。提供了与 Vue 应用的更直接的交互能力。

    • 返回:

    • vueAppApp,vue 的实例对象 app。

      js
      export default defineNuxtPlugin((nuxtApp) => {
        // 注册全局组件
        nuxtApp.vueApp.component('MyGlobalComponent', MyGlobalComponent)
      
        // 注册全局指令
        nuxtApp.vueApp.directive('focus', {
          mounted(el) {
            el.focus()
          }
        })
      
        // 集成其他库
        const pinia = createPinia()
        nuxtApp.vueApp.use(pinia)
      })
  • 方法

  • nuxtApp.provide()(key, value),用于将数据或功能注入到应用程序中的一个方法。此处用于注册插件。这是通过 Vue 3 的依赖注入机制实现的,允许你在组件树中共享数据而无需逐层传递 props。

    • keystring,表示要提供的名称。

    • valueany,表示要注入的值。

      js
      // 1. 注册插件
      export default defineNuxtPlugin((nuxtApp) => {
        const myFunction = () => {
          console.log('Hello from myFunction!')
        }
      
        // 注入插件函数到应用中
        nuxtApp.provide('myFunction', myFunction)
      })
      js
      // 2. 在组件中使用注册的插件
      import { useNuxtApp } from '#app'
      const { $myFunction } = useNuxtApp()
      const callMyFunction = () => {
        $myFunction() // 将会输出 "Hello from myFunction!"
      }
  • nuxtApp.hook()(name, callback),用于注册钩子函数的方法。

    • namestring,钩子的名称,表示在什么事件触发时会执行该回调函数。

    • callbackfunction,当钩子被触发时调用的回调函数。

      js
      export default defineNuxtPlugin((nuxtApp) => {
        // App created hook
        nuxtApp.hook('app:created', () => {
          console.log('Application has been created.')
        })
      
        // App mounted hook
        nuxtApp.hook('app:mounted', () => {
          console.log('Application has been mounted.')
        })
      
        // Route before each hook
        nuxtApp.hook('route:beforeEach', (to, from) => {
          console.log(`Navigating from ${from.fullPath} to ${to.fullPath}`)
        })
      })
  • nuxtApp.callHook()(hookName, ...args),用于手动触发钩子函数的方法。

    • hookNamestring,表示需要调用的钩子的名称。

    • ...argsany,传递给钩子回调函数参数的数据。

      js
      export default defineNuxtPlugin((nuxtApp) => {
        nuxtApp.hook('route:beforeEach', (to, from) => {
          if (to.path === '/special') {
            // 手动调用其他钩子
            nuxtApp.callHook('special-route-enter', { route: to })
          }
        })
      
        nuxtApp.hook('special-route-enter', ({ route }) => {
          console.log(`Entering special route: ${route.fullPath}`)
        })
      })
  • defineNuxtPlugin()(fn | obj),用于创建和注册插件的函数。

    • fn(nuxtApp) => void,函数式写法。

    • obj{name, enforce, setup, hooks, env},对象式写法。

      js
      // 函数式写法
      export default defineNuxtPlugin((nuxtApp) => {
        // 在这里可以设置你的插件逻辑
      
        // 例如,向 nuxtApp 注入一个全局的 API
        nuxtApp.provide('myApi', () => {
          return {
            fetchData: async () => {
              // 模拟 API 调用
              return await fetch('/api/data').then((res) => res.json())
            }
          }
        })
      })
      js
      // 对象式写法
      export default defineNuxtPlugin({
        name: 'my-plugin',
        enforce: 'pre', // 或 'post'
        async setup(nuxtApp) {
          // 这相当于一个普通的功能性插件
        },
        hooks: {
          // 这里可以直接注册Nuxt应用运行时钩子
          'app:created'() {
            const nuxtApp = useNuxtApp()
            //
          }
        },
        env: {
          // 如果不希望插件在仅渲染服务器端或孤立组件时运行,请将此值设置为`false`。
          islands: true
        }
      })

创建插件:

Nuxt3 支持自定义插件进行扩展。

useNuxtApp()提供了访问 Nuxt 共享运行时上下文的方法和属性(两端可用):provide、hooks、callhook、vueApp 等。

方式一:使用 useNuxtApp() 中的 provide(name, value) 方法在 app.vue 中直接创建。

  • app.vue中创建插件

    image-20241009142221653

  • 可以在app.vue或其他页面中访问注册的插件

    image-20241009142245756

方式二:在 plugins 目录中通过 defineNuxtPlugin() 创建插件(推荐)

1、在 plugins 目录下使用defineNuxtPlugin()创建 plugins/price.ts 插件,参数是一个回调函数。

image-20241009145041313

2、然后在组件中使用 useNuxtApp() 创建的实例对象nuxtApp.$xxx来访问插件中的方法。

image-20241009145017069

自动注册:

  • plugins里面的所有插件都会自动注册,你不需要单独将它们添加到你的nuxt.config中。

  • 只有plugins目录的顶层文件或任何子目录中的索引文件才会自动注册为插件。

    bash
    -| plugins/
    ---| foo.ts      // 被扫描
    ---| bar/
    -----| baz.ts    // 不被扫描
    -----| foz.vue   // 不被扫描
    -----| index.ts  // 目前被扫描,但已弃用

区分环境:可以在文件名中使用.server.client后缀,只在服务器端或客户端上加载插件。如price.client.ts

image-20241009145950266

注册顺序:可以通过在文件名前面添加“字母”编号来控制插件的注册顺序。

bash
plugins/
 | - 01.myPlugin.ts
 | - 02.myOtherPlugin.ts

生命周期

API

  • nuxtApp.hook()(name, callback),用于注册钩子函数的方法。

    • namestring,钩子的名称,表示在什么事件触发时会执行该回调函数。

    • callbackfunction,当钩子被触发时调用的回调函数。

      js
      export default defineNuxtPlugin((nuxtApp) => {
        // App created hook
        nuxtApp.hook('app:created', () => {
          console.log('Application has been created.')
        })
      
        // App mounted hook
        nuxtApp.hook('app:mounted', () => {
          console.log('Application has been mounted.')
        })
      
        // Route before each hook
        nuxtApp.hook('route:beforeEach', (to, from) => {
          console.log(`Navigating from ${from.fullPath} to ${to.fullPath}`)
        })
      })
  • nuxtApp.callHook()(hookName, ...args),用于手动触发钩子函数的方法。

    • hookNamestring,表示需要调用的钩子的名称。

    • ...argsany,传递给钩子回调函数参数的数据。

      js
      export default defineNuxtPlugin((nuxtApp) => {
        nuxtApp.hook('route:beforeEach', (to, from) => {
          if (to.path === '/special') {
            // 手动调用其他钩子
            nuxtApp.callHook('special-route-enter', { route: to })
          }
        })
      
        nuxtApp.hook('special-route-enter', ({ route }) => {
          console.log(`Entering special route: ${route.fullPath}`)
        })
      })

App 生命周期

App Hooks主要由Nuxt 插件用于挂接渲染生命周期,但也可在 Vue 组合式中使用。

语法:

js
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('page:start', () => {
    /* 在这里写入你的代码 */
  })
})

app 生命周期钩子:

钩子参数环境描述
app:createdvueApp服务器端和客户端在初始 vueApp 实例创建时调用。
app:errorerr服务器端和客户端在发生致命错误时调用。
app:error:cleared{ redirect? }服务器端和客户端在发生致命错误时调用。
app:data:refreshkeys?服务器端和客户端(内部)
vue:setup-服务器端和客户端(内部)
vue:errorerr, target, info服务器端和客户端当 vue 错误传播到根组件时调用。了解更多
app:renderedrenderContext服务器端在 SSR 渲染完成时调用。
app:redirected-服务器端在 SSR 重定向之前调用。
app:beforeMountvueApp客户端在应用程序挂载之前调用,仅在客户端端调用。
app:mountedvueApp客户端在 Vue 应用程序初始化并在浏览器中挂载时调用。
app:suspense:resolveappComponent客户端Suspense 解析事件上调用。
link:prefetchto客户端当观察到 <NuxtLink> 被预取时调用。
page:startpageComponent?客户端Suspense 挂起事件上调用。
page:finishpageComponent?客户端Suspense 解析事件上调用。
page:transition:finishpageComponent?客户端在页面过渡的 onAfterLeave 事件之后调用。

示例:plugins目录的插件中监听 hooks。

image-20241009161147194

在组件的 setup 标签中的 hooks 无法监听 app:createapp:beforeMountvue:setup ,因为此时这些 hooks 已经触发完毕。

image-20241009161621536

组件生命周期

组件生命周期语法与Vue一样。

因为没有任何动态更新,所以像 onMounted 或者 onUpdated 这样的生命周期钩子不会在 SSR 期间被调用,而只会在客户端运行。

你应该避免在 setup() 或者 <script setup> 的根作用域中使用会产生副作用且需要被清理的代码。这类副作用的常见例子是使用 setInterval 设置定时器。我们可能会在客户端特有的代码中设置定时器,然后在 onBeforeUnmountonUnmounted 中清除。然而,由于 unmount 钩子不会在 SSR 期间被调用,所以定时器会永远存在。为了避免这种情况,请将含有副作用的代码放到 onMounted

组件生命周期钩子在客户端服务端触发情况:

钩子钩子(组合式)客户端服务端
beforeCreate-
setup
created-
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted

网络请求

API

注意: 以下函数只能用在 setup 生命周期钩子 中使用。

  • $fetch()()
    • :``,
    • :``,
    • 返回:
    • :``,
  • useFetch()()
    • :``,
    • :``,
    • 返回:
    • :``,
  • useAsyncData()()
    • :``,
    • :``,
    • 返回:
    • :``,
  • useLazyFetch()()
    • :``,
    • :``,
    • 返回:
    • :``,
  • useLazyAsyncData()()
    • :``,
    • :``,
    • 返回:
    • :``,

获取数据

方式一$fetch()用于在Vue应用程序或API路由中进行HTTP请求。

image-20241009170950949

问题: 在组件中使用 $fetch() 而不使用useAsyncData进行包装会导致发送两次数据请求。

解决:建议在获取组件数据时使用useFetch()useAsyncData() + $fetch()来防止重复获取数据。

方式二useAsyncData() + $fetch()

image-20241009172827546

发送请求时机:

  • useAsyncData()在刷新页面时在服务端发送请求,客户端不发送。

  • useAsyncData()在站内路由切换时在服务端不会发送请求,客户端发送请求。

保证key值唯一:

  • 当key值重复,页面刷新(此时是服务端发起的请求,客户端通过水合得到数据)时请求到的数据也会重复。

    image-20241010144503411

    image-20241010144507504

  • 当key值重复,站内路由跳转(此时是客户端发起的请求)时,请求到的数据不会重复。

方式三useFetch() useAsyncData() + $fetch()的简写。(推荐

image-20241009174629140

请求选项:

ts
type UseFetchOptions<DataT> = {
  key?: string
  method?: string
  query?: SearchParams
  params?: SearchParams
  body?: RequestInit['body'] | Record<string, any>
  headers?: Record<string, string> | [key: string, value: string][] | Headers
  baseURL?: string
  server?: boolean
  lazy?: boolean
  immediate?: boolean
  getCachedData?: (key: string) => DataT
  deep?: boolean
  default?: () => DataT
  transform?: (input: DataT) => DataT
  pick?: string[]
  watch?: WatchSource[] | false
}

image-20241009175121049

拦截器:

js
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 }) {
    // 处理响应数据
    localStorage.setItem('token', response._data.token)
    // response._data的数据会返回出去给{ data }
    // return返回的数据不会返回出去给{ data }
  },
  onResponseError({ request, response, options }) {
    // 处理响应错误
  }
})

懒加载: useFetch() 默认会阻塞页面的导航。可以通过lazy选项控制是否懒加载。

  • 通过watch()监听获取异步加载的数据。

    image-20241009220403239

  • 通过调用refresh()刷新客户端重新发送请求。通过pending显示网络请求所处状态。

    image-20241009222509802

  • 通过修改请求参数,重新发送网络请求。

    image-20241009222737192

方式四useLazyFetch()useFetch()懒加载模式的简写。

image-20241009220659750

useFetch VS axios

获取数据 Nuxt 推荐使用 useFetch 函数,为什么不是 axios ?

  • useFetch 底层调用的是$fetch 函数,该函数是基于 unjs/ofetch 请求库,并与原生的 Fetch API 有者相同 API。
  • unjs/ofetch 是一个跨端请求库: A better fetch API. Works on node, browser and workers。
    • 如果运行在服务器上,它可以智能的处理对 API 接口的直接调用
    • 如果运行在客户端上,它可以对后台提供的 API 接口正常的调用(类似 axios),当然也支持第三方接口的调用
    • 自动解析响应对数据进行字符串化
  • useFetch 支持智能的类型提示智能的推断 API 响应类型
  • 在 setup 中用 useFetch 获取数据,会减去客户端重复发起的请求

image-20240911152920968

useFetch 封装

1、定义 HYRequest 类,并定义request方法。

image-20240911152934257

2、在类中定义 get、post 方法

image-20241009225846606

3、在 request 中使用 useFetch 发起网络请求

image-20241010101221431

image-20241010101313457

image-20241010101156888

4、添加 TypeScript 类型声明

服务端接口

Nuxt3 提供了编写后端服务接口的功能,编写服务接口可以在 server/api 目录下编写。

语法:

文件系统路由: 存放在以下目录中的文件遵循文件系统路由

  • ~/server/api:这里的文件在路由中添加/api前缀。
  • ~/server/routes:这里的文件不会在路由中添加/api前缀。

API: 通过 defineEventHandler() 定义API接口。

区分请求方法: 为文件名添加.get后缀。

实现过程: 编写一个 /api/homeinfo 接口

1、在 server/api 目录下新建 homeinfo.ts,并使用 defineEventHandler() 函数定义接口(支持 async)。

image-20241010103430504

2、在浏览器中通过http://localhost:3000/api/homeInfo访问该接口。

image-20241010102858476

3、在组件中使用 useFetch() 函数调用/api/homeinfo接口。

image-20241010103132619

示例: 添加GET请求

image-20241010104101997

image-20241010104322255

示例: 添加POST请求,模拟登录

1、在~/server/api/login.post.ts中添加登录接口

image-20241010105447641

2、在登录组件中发起登录请求,并保存返回的token到cookie中,之后跳转到首页。

image-20241010105521669

3、在首页中获取保存的cookie信息

image-20241010105651314

状态管理

useState()

Nuxt 跨页面、跨组件全局状态共享可使用 useState(支持 Server 和 Client ):

语法:

image-20241010150315406

参数:

  • init:为状态提供初始值的函数,该函数也支持返回一个 Ref 类型
  • key: 唯一 key,确保在跨请求获取该数据时,保证数据的唯一性。为空时会根据文件和行号自动生成唯一 key

返回值: Ref 响应式对象

基本使用:

1、在 composables 目录下创建useCounter.ts,并通过useState() 定义需全局共享的状态并导出。

  • hook名由文件名决定。

    image-20241010154102187

  • hook名由导出的命名函数名决定。

    image-20241010154258744

2、在组件中导入 states.ts 导出的全局状态。

image-20241010154208692

其他类型值:

对象类型:

image-20241010155508502

image-20241010155623098

image-20241010155656544


注意:

  • useState 只能用在 setup 函数 和 Lifecycle Hooks 中。
  • useState 不支持 class, functionor symbol类型,因为这些类型不支持序列化。

Pinia

集成Pinia

依赖包:

  • @pinia/nuxt:会处理 state 同步问题,比如不需要关心序列化或 XSS 攻击等问题。

    • 安装:npm install @pinia/nuxt –-save
  • pinia:如有遇到 pinia 安装失败,可以添加 --legacy-peer-deps--force告诉 NPM 忽略对等依赖并继续安装。或使用 yarn。

    • 安装:npm install pinia –-save

基本使用:

1、安装依赖包。

2、在 nuxt.config 文件中添加: modules: ['@pinia/nuxt']

image-20241010161459023

3、创建pinia模块useHomeStore

image-20241010161926946

4、在组件中获取store中的数据

image-20241010161941309

TS类型

image-20241010162152592

异步请求

1、在store中发送异步请求

image-20241010163652329

2、在组件中触发store中的异步action

image-20241010163722868

对比useState()

Nuxt 跨页面、跨组件全局状态共享,既可以使用 useState,也可以使用 Pinia,那么他们有什么异同呢?

共同点:

  • 都支持全局状态共享,共享的数据都是响应式数据
  • 都支持服务器端和客户端共享

Pinia优势: Pinia 比 useState 有更多的优势

  • 开发工具Devtools支持:
    • 跟踪动作,更容易调试
    • store 可以出现在使用它的组件中
  • 模块热替换:
    • 无需重新加载页面即可修改 store 数据
    • 在开发时保持任何现有状态
  • 插件:可以使用插件扩展 Pinia 功能
  • TypeScript支持:提供适当的 TypeScript 支持或自动完成