Vue SSR 渲染 Nuxt3 入门学习

SPA应用:也就是单页应用,这些多是在客户端的应用,不利于进行SEO优化(搜索引擎优化)。

SSR应用:在服务端进行渲染,渲染完成后返回给客户端,每个页面有独立的URL,对SEO友好。

Nuxt3是基于Vue3发布的SSR框架

nuxt3 特性:

  • 更轻量:以现代浏览器为基础的情况下,服务器部署和客户端产物最多减小75倍。
  • 更快:用动态服务端代码来优化冷启动。
  • Hybird:增量动态生成和其他高级模式现在都成为可能。
  • Suspense: 导航前后可在任何组件中获取数据。
  • Composition API : 使用Composition API 和 Nuxt3的composables 实现真正的可复用性。
  • Nuxt CLI : 权限的零依赖体验,助你轻松搭建项目和集成模块。
  • Nuxt Devtools :专属调试工具,更多的信息和快速修复,在浏览器中高效工作。
  • Nuxt Kit :全新的基于 TypeScript 和跨版本兼容的模块开发。
  • Webpack5 : 更快的构建速度和更小的构建包,并且零配置。
  • Vite:用Vite作为你的打包器,体验轻量级的快速HMR。
  • Vue3 : 完全支持Vue3语法,这一点特别关键。
  • TypeScript:由原生TypeScript和ESM构成,没有额外配置步骤。

开始动手

//  1, 构建第一个项目, 多使几次,容易网络问题构建不成功
npx nuxi init nuxt-app
//  2. cd 进入 nuxt-app 安装依赖包使用 npm 或者 yarn
npm i  /  yarn 
// 3, 启动 nuxt.js
npm run dev

在这里插入图片描述
项目跑起来后点击链接打开展示出这个 nuxt 页面,项目就跑起来了
在这里插入图片描述

nuxt 的目录结构

- .nuxt               // 自动生成的目录,用于展示结果
- node_modules        // 项目依赖包存放目录
- .gitignore          // Git的配置目录,比如一些文件不用Git管理就可以在这个文件中配置
- app.vue             // 项目入口文件,你可以在这里配置路由的出口
- nuxt.config.ts      // nuxt项目的配置文件 ,这个里边可以配置Nuxt项目的方法面面
- package-lock.json   // 锁定安装时包的版本,以保证其他人在 npm install时和你保持一致
- package.json        // 包的配置文件和项目的启动调式命令配置
- README.md           // 项目的说明文件
- tsconfig.json       // TypeScript的配置文件

随着我们的开发目录也会越来越多,比如常用的还有下面三个目录。

- pages               // 开发的页面目录
- components          // 组件目录
- assets              // 静态资源目录
- layouts             // 项目布局目录

接下来写个 HelloWorld 网页
在根目录下创建一个 components 文件夹用来存放组件
1, 在 components 文件下新建 HelloWorld.vue 组件

// HelloWorld.vue
<template>
  <h1>HelloWorld</h1>
</template>

2, 在 app.vue 中使用组件,没错! 不用引入! 可以直接使用

// app.vue

<template>
    <hello-world></hello-world>
</template>

3, 页面效果
在这里插入图片描述

约定路由

如何在 Nuxt 中展示页面还有路由?
1.首先先在根目录新建一个 pages 文件夹,里面创建一个 Index.vue 和 About.vue


 <!--Index.vue  -->
 <!--About.vue同理  -->
 
<template>
  <div>
    <h1>Index.vue</h1>
    <!-- 使用NuxtLink 来跳转到其他的路由 -->
    <!-- 文件名叫About.vue对应的路由就是 /About-->
    <NuxtLink to="/About">About.vue</NuxtLink>
  </div>
</template>

<script setup>
import {} from "vue";
</script>

<style scoped></style>

2.在 app.vue 中添加 NuxtPage 路由组件

<template>
  <div>
    <hello-world></hello-world>
    <!-- 路由组件 -->
    <NuxtPage></NuxtPage>
  </div>
</template>

页面输入 /Index 就能展示出页面了

在这里插入图片描述
Nuxt 框架不鼓励我们使用 a 标签来跳转路由,推荐使用 NuxtLink 来跳转,比如在 index 页跳转到 about 页

<!-- index.vue -->
<template>
  <div>
    <h1>Index.vue</h1>
    <!-- 使用NuxtLink 来跳转到其他的路由 -->
    <!-- 文件名叫About.vue对应的路由就是 /About-->
    <NuxtLink to="/About">About.vue</NuxtLink>
  </div>
</template>

nuxt 动态路由的写法

nuxt 里面的动态路由需要创建 文件名[路由参数] 这种格式的vue文件,例如 list-[id].vue

<!-- list-[id].vue-->
<template>
  <div>
   <!--渲染到页面上-->
    {{$route.params.id}}
  </div>
</template>

在index.vue里面写入需要跳转的路由

<!-- index.vue-->
<template>
  <div>
    <div>index.vue</div>
    <NuxtLink to="/about">跳转关于</NuxtLink>
    <!--跳转到动态路由-->
    <NuxtLink to="/list-4">跳转列表页</NuxtLink>
  </div>
</template>

<script setup>
import {} from "vue";
</script>

<style scoped></style>

运行项目,已经获取到 id 了
在这里插入图片描述
那我们需要两个或者多个动态参数怎么办?
在根目录下新建一个文件夹,格式跟创建动态路由网页一样, 文件夹名[动态参数],例如 goods-[name]
在这里插入图片描述
在 index.vue 写入跳转链接, list-[id].vue 里面获取name参数

<!-- index.vue-->
<template>
  <div>
    <div>index.vue</div>
    <NuxtLink to="/about">跳转关于</NuxtLink>
    <!--跳转到动态路由-->
    <NuxtLink to="/goods-phone/list-4">跳转列表页</NuxtLink>
  </div>
</template>
<!-- list-[id].vue-->
<template>
  <div>
   <!--渲染到页面上-->
    {{$route.params.id}}
    {{$route.params.name}}
  </div>
</template>

就可以拿到 name 参数跟 id 参数了
在这里插入图片描述

nuxt 嵌套路由的写法

跟动态路由类似,在根目录创建一个与父页面同名的文件夹,比如父页面是 parent.vue, 那就在根目录创建一个 parent 文件夹
在这里插入图片描述
在 parent.vue 父页面里面加入 NuxtChild 组件,即可在父页面里面动态展现子页面

<template>
  <div>
    <div class="parent">我是parent.vue 父页面</div>
    <!-- 跳转路由 -->
    <NuxtLink to="/parent/child">展示/parent/child页面</NuxtLink>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    <NuxtLink to="/parent/son">展示/parent/son页面</NuxtLink>
    <!-- 子页面的展示 -->
    <NuxtChild></NuxtChild>
  </div>
</template>

<script setup>
import {} from "vue";
</script>

<style scoped></style>

在 parent 文件夹里面新增子页面 child.vue 跟 son.vue 文件

<!-- child.vue -->
<template>
  <div class="child">这里是child.vue页面</div>
</template>

<script setup>
import {} from "vue";
</script>

<style scoped></style>

运行起来就可以实现嵌套了
在这里插入图片描述

使用 layout 布局模板

nuxt3 提供了一种布局架构给我们,可以在整个应用中使用,可以用来放重复的常见的UI,代码模式,提高代码重用性,降低代码复杂度,把通用的代码提取出来,页面使用的时候会异步导入进来。

layout 的使用
在根目录创建一个 layouts 布局文件夹,然后在 layouts 文件夹下面创建一个 input.vue 模板

在这里插入图片描述
在 input.vue 中使用 slot 插槽

<template>
  <div class="input">
    <!-- icon图标 -->
    <slot name="icon"></slot>
    <input type="text" />
    <button>
      <!-- 按钮字体 -->
      <slot name="btn-text"></slot>
    </button>
  </div>
</template>

<script setup>
import {} from "vue";
</script>

<style scoped></style>

在根目录中创建一个 form.vue 页面, 并且使用 NuxtLayout 组件, name 对应的是 layout 文件里面模板的名字

<template>
  <div>
    <!-- 使用NuxtLayout组件, name对应模板的名字 -->
    <NuxtLayout name="input">
        <template #icon>
            <!-- icon图标 -->
            <i>X</i>
        </template>
        <!-- 按钮文字 -->
        <!-- 使用 template 加上 #插槽名字 -->
        <template #btn-text>
            提交
        </template>
    </NuxtLayout>
  </div>
</template>

<script setup>
import {} from 'vue'
</script>

<style scoped></style>

运行项目,我们已经传入 x 图标跟动态传递文字了
在这里插入图片描述

nuxt 组件的使用

在根目录中创建 components 文件夹, 把要复用的组件编写在里面,在components中创建 footer.vue
在这里插入图片描述

<!-- footer.vue-->
<template>
  <div class="footer">
    <div>copy right 2022 zheng_jia_jun</div>
  </div>
</template>


<script setup>
import {} from "vue";
</script>

<style scoped></style>

创建好后直接在需要使用该组件的页面里面使用即可,推荐大写组件

<template>
  <div>
    <h1>Index.vue</h1>
    <!-- 跳转导航 -->
    <NuxtLink to="/About">About.vue</NuxtLink>
    <!--底部组件-->
    <Footer></Footer>
  </div>
</template>

<script setup>
import {} from "vue";
</script>

<style scoped></style>

页面上即可使用, layouts 里面的布局也是同理,就不做演示了
在这里插入图片描述

多层级组件的使用

我们在开发的时候组件肯定要用文件夹分类起来,不可能直接写在 components目录,需要将不同功能的组件分类在各自的文件夹中,看示例在这里插入图片描述
这个时候在页面中导入就不是 icon 这样子的了,nuxt 规定 文件夹+加上文件名字 导入

<template>
  <div>
    <h1>Index.vue</h1>
    <!-- 跳转导航 -->
    <NuxtLink to="/About">About.vue</NuxtLink>
    <Footer></Footer>
    <!-- 使用嵌套组件 -->
    <NavIcon></NavIcon>
  </div>
</template>

<script setup>
</script>

<style scoped></style>

组件懒加载

在组件的前面加上 Lazy前缀即可使用懒加载,懒加载组件的目的是在项目打包的时候包更小。简单理解可以理解为只有在组件显示在页面上时才进行加载。 比如我们需要点击按钮显示或者隐藏某个组件

<!-- index.vue -->
<template>
  <div>
    <h1>Index.vue</h1>
    <!-- 跳转导航 -->
    <NuxtLink to="/About">About.vue</NuxtLink>
    <Footer></Footer>
    <!-- 使用嵌套组件 -->
    <NavIcon></NavIcon>
    <!-- 使用懒加载的组件 -->
    <LazyNavIcon v-show="isShow"></LazyNavIcon>
    <button @click="isShow = !isShow">切换显示隐藏</button>
  </div>
</template>

<script setup>
import {ref} from "vue";
// 默认不显示
const isShow = ref(false)
</script>

<style scoped></style>

nuxt 中使用复用型逻辑代码,composables 文件夹的使用

开发中我们经常重复使用一些逻辑代码,会把他们封装在一个utils 文件夹下,Nuxt 也提供了一个 composables 文件夹,在此文件夹下创建的一级目录文件会将里面的方法自动导入到全局应用,就是在每个页面里面都可以使用到这个代码。
在这里插入图片描述

// time.ts 
// export 的话使用时用的是对应导出的名字 getTime
// export default 默认导出的话用的是 time.ts 这个文件的名字 time
export function getTime(){
    let time = new Date()
    return time.toUTCString()
}

然后在每个页面中即可使用这些方法,这个方法写太多的话影响性能,因为每个方法都会导入到全局

<!-- index.vue -->
<template>
  <div>
    <h1>Index.vue</h1>
    <!-- 在代码中使用 composables 的逻辑代码 -->
    <div>{{getTime()}}</div>
  </div>
</template>

<script setup>
// 使用componables 通用逻辑代码
console.log(getTime())
// 实现搜索功能
inputSearch()
</script>

<style scoped></style>

在 Nuxt 中 服务端获取请求数据

因为Nuxt3是SSR的方案,所以你可能不仅仅只是想要在浏览器端发送请求获取数据,还想在服务器端就获取到数据并渲染组件。

Nuxt3 提供了4种方式使得你可以在服务器端异步获取数据,他们只能在生命周期函数里面使用

  • useAsyncData
  • useFetch
  • useLazyAsyncData
  • useLazyFetch

后面两个 Lazy 只是在前面两个的配置上配置了 { lazy: true }, 其他的功能都跟默认一样

<!-- http.vue -->
<template>
  <div>http.vue</div>
</template>

<script setup>
import {} from "vue";
// api
let url = "https://interface.meiriyiwen.com/article/today?dev=1";
// 官方推荐使用 await 来解决异步问题
let res = await useAsyncData(
  "getList",
  () => {
    return $fetch(url);
  },
  {
    lazy: true,
  }
);
console.log(res);

// 使用 useFetch 请求
useFetch(url, {
  method: "get",
  id: 1,
}).then((res) => {
  console.log("useFetch:",res);
});
</script>

<style scoped>
</style>

返回的参数格式

// useFetch 的返回对象同理
const {
  data: Ref<DataT>,// 返回的数据结果
  pending: Ref<boolean>,// 是否在请求状态中
  refresh: (force?: boolean) => Promise<void>,// 强制刷新数据
  error?: any // 请求失败返回的错误信息
} = useAsyncData(
  key: string, // 唯一键,确保相同的请求数据的获取和去重
  fn: () => Object,// 一个返回数值的异步函数
  options?: { lazy: boolean, server: boolean }
  // options.lazy,是否在加载路由后才请求该异步方法,默认为false
  // options.server,是否在服务端请求数据,默认为true
  // options.default,异步请求前设置数据data默认值的工厂函数(对lazy:true选项特别有用)
  // options.transform,更改fn返回结果的函数
  // options.pick,只从数组中指定的key进行缓存
)

Nuxt 中使用全局或者局部中间件

在根目录下创建 middleware 文件夹, 并且在里面创建中间件, 如果文件加上 .global 后缀的话,就是全局的中间件,不加的话就是自定义在哪些页面的中间件

在这里插入图片描述
定义好 default.global.ts 全局中间件就已经可以使用了

// default.global.ts
// 一个简单的路由守卫
export default function defineNuxtRouterMiddleware(to, from) {
    console.log("要去往的路径:" + to.path)
    console.log("我来的路径:" + from.path)
    if (to.path == '/About') {
        // 停止导航
        abortNavigation()
        console.log('暂无权限跳转到/about')
        return navigateTo('/Index')
    }
}
// listen.ts 自定义页面使用
export default function defineListenMiddleware(to, from) {
    console.log("监听用户操作")
}

自定义在指定的页面使用中间件,需要使用 MiddlePageMeta配置

<template>
  <div>middleware.vue</div>
</template>

<script setup>
import {ref} from "vue";

definePageMeta({
	// 专属这个页面的监听器
  middleware: ["listen"],
});
</script>

<style scoped></style>

在这里插入图片描述

nuxt 中使用 useState 创建状态

Nuxt 提供可组合的 useState 来创建跨组件的并且对 SSR 友好的响应式状态。
useState 是对 ssr 友好的共享状态替代品,它的值将会在服务端渲染后保留,并且使用唯一的键在各个组件中共享。

useState 只允许在 setup生命周期函数里面使用

useState参数

  • key :唯一的键确保数据请求能够正确并且不被重复
  • init :给 state 初始化时候的一个默认值
  • T :(仅用作于 typescript )描述 state 的类型
useState<T>(key: string, init?: () => T): Ref<T>

示例

<script setup>
// 创建对ssr友好的响应式状态
const counter = useState("counter", () => Math.round(Math.random() * 1000));
</script>

<template>
  <div>
    Counter: {{ counter }} Name: {{ name }}
    <button @click="counter++">+</button>
    <button @click="counter--">-</button>
  </div>
</template>

使用 useState 创建共享状态

如果我们要使用共享状态,可以利用 componsables 文件夹,在这个文件夹里面创建一个 states.ts, 然后定义不同的共享状态分别导出

// /componsables/states.ts
// 全局共享状态
export const useInfo = () => useState("info",() => {
    return {
        id: 1,
        name: '用户1'
    }
})
// 用户的 token
export const useToken = () => useState("token",()=> "tokenXXXXXXXXXXXXXXX") 

在页面中使用

<!-- state.vue -->
<script setup>
// 使用共享状态
const info = useInfo();
const token = useToken();
</script>

<template>
  <div>
    <div class="user-info">
      <p>
      {{ info.id }}
      {{ info.name }}
      </p>
      <p>
        token: {{ token }}
      </p>
    </div>
  </div>
</template>

页面中已经可以访问到共享状态了
在这里插入图片描述

运行时获取配置 RuntimeConfig

Nuxt 提供了一个 API,用于在应用程序和 API 路由中定义运行时配置。为了向应用程序公开配置跟环境变量,我们需要把配置定义在 nuxt.config.ts 里面,配置分为 公开跟私有配置,可以使用,publicRuntimeConfig 或者 privateRuntimeConfig 选项(根据用户是否能访问这些部分配置来使用)

// nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'

// https://v3.nuxtjs.org/api/configuration/nuxt.config
// 定义 config
export default defineNuxtConfig({
    // 公开的配置
    publicRuntimeConfig: {
       Base_Url: 'https://zhengjiajun.com:55'
    },
    // 传递给服务端的,不公开的,返回服务端会把 public 跟 private 两个对象合并上传,private会覆盖public
    privateRuntimeConfig: {
       Base_Url: 'https://zhengjiajun.com:66'
    }
})

在这里插入图片描述
服务端获取到的是 66 端口,privateRuntimeConfig 会覆盖 publicRuntimeConfig
在这里插入图片描述

访问存放的 RuntimeConfig
在 Nuxt 应用程序的 Vue 实例中,需要调用 useRuntimeConfig() 来访问运行时配置。

<!-- config.vue -->
<template>
  <div>
   configUrl: {{config.Base_Url}}
  </div>
</template>

<script setup>
import {ref} from 'vue'
// 获取config
const config = useRuntimeConfig()
console.log(config.Base_Url)
</script>

<style scoped></style>

在路由api中使用 nuxt.config

// /server/api/hello.ts
export default (req, res) => {
    // 使用 useRuntimeConfig
    return `hello-${useRuntimeConfig().Base_Url}`
}

nuxt 获取 NuxtApp 上下文

在组合函数、组件以及插件中通过 useNuxtApp 访问 nuxtApp 实例。

<template>
  <div>
    <div>nuxtapp</div>
    <NuxtLink to="/Index">/index</NuxtLink>
  </div>
</template>

<script setup>
// 导入 useNuxtApp
import { useNuxtApp } from "#app";
// 获取 nuxtapp 上下文
const NuxtApp = useNuxtApp();
// 类似于注入
NuxtApp.provide("jun", ()=>"注入");
// 通过NuxtAPP."$名字" 即可在应用中访问
console.log(NuxtApp.$jun);
</script>

<style scoped></style>

运行项目
在这里插入图片描述
nuxt 中使用 useCookie

<template>
  <div>cookie.vue</div>
</template>

<script setup>
import {} from "vue";
// 添加一个 cookie
const userId = useCookie("userId", {
    // 设置默认值
  default: () => "123456",
  // 10秒后过期
  // 单位秒。默认不设置最大超时时间。
  maxAge: 10,
  // 默认不设置该值。 大部分客户端将它视为“持久化的cookie”
  expires: new Date('2022-7-20')// secure 只允许 https 链接传递 cookie,默认为false
  secure: false,
  // httpOnly 设置 是否允许客户端通过 js 获取到 cookie,默认为false
  httpOnly: false,
  // 允许访问该 cookie 的域名 不设置默认是当前浏览器域名
  domain: "localhost",
  // 	Cookie的使用路径。如果设置为“/sessionWeb/”,
  // 则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。
  // 如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/”。
  path: "/",
});
console.log(userId);
// 判断浏览器cookie是否已经有 userId
userId.value = userId.value || Math.round(Math.random() * 1000);
</script>

<style scoped></style>

nuxt 中 路由 api 使用 cookie
通过 h3 包中的 useCookie 跟 setCookie 在服务器 API 路由中访问跟设置 cookie

// server/api/cookie.ts
import { useCookie, setCookie } from "h3"

export default defineEventHandler(event => {
    // 查看有没有这个 cookie
    let counter = useCookie(event, 'counter') || 0

    // 设置 cookie + 1
    setCookie(event, 'counter', ++counter)

    // 返回 json 格式
    return { counter }
})

在 localhost:3000/api/cookie 即可看到效果

在这里插入图片描述

Nuxt3 中创建使用插件

Nuxt 将自动读取"plugins"目录中的文件并加载它们。如果仅想在服务器端或客户端加载插件时,可以在文件名中使用.server或.client后缀。
plugins 目录跟 componables 目录一样,只渲染一级目录的插件,例如

plugins
 | - myPlugin.ts
 | - myOtherPlugin
 | --- supportingFile.ts
 | --- componentToRegister.vue
 | --- index.ts

只注册 myPlugin.ts 和 myOtherPlugin/index.ts

创建插件 在根目录下创建 plugins ,新建 helps.ts

// 使用 defineNuxtPlugin 创建插件
import { defineNuxtPlugin } from "#app";
// nuxt3 规定只能给插件传递 nuxtApp 上下文参数,
// 可以对 nuxtApp 做您想要做的操作比如做一个加法函数
export default defineNuxtPlugin((nuxtApp) => {
    return {
    	// 如果需要注入 nuxtApp,可以直接返回 provie 对象即可使用
    	// 跟 nuxtApp.provide(add,() => (a,b) => a+b)  相同
        provide:  {
            add: (a,b) => a + b  
        }
    }
})

在页面中访问 nuxtApp 注入的 add 函数

<template>
  <div>
    1 + 2 = {{useNuxtApp().$add(1,2)}}
  </div>
</template>

<script setup>
import {} from 'vue'
</script>

<style scoped></style>

效果已经出来了
在这里插入图片描述

nuxt3 中 使用 路由 api 接口

Nuxt 将自动读取~/server/api目录中的任何文件,以创建 API 端点。
每个文件都应导出一个处理 API 请求的默认函数。它可以直接返回承诺或 JSON 数据(或使用 )。
在 根目录下创建 server/api/getList.ts

// 每个文件都应导出一个处理 API 请求的默认函数。它可以直接返回承诺或 JSON 数据
// server/api/getList.ts
export default async (req, res) => {
    let url = "https://interface.meiriyiwen.com/article/today?dev=1";

    // 获取异步数据
    let data ;
    await fetch(url, {
        method: 'get'
    }) .then(response => response.json())
    .then(res => data = res);

    // 返回数据
    return {
        data
    }
}

访问 http://localhost:3000/api/getList 即可看到返回数据
在这里插入图片描述

Logo

前往低代码交流专区

更多推荐