1.邂逅SPA、SSR
1.1 单页面应用程序
单页应用程序 (SPA) 全称是:Single-page application,SPA应用是在客户端呈现的(术语称:
CSR
(Client Side Render))
SPA的优点
- 只需加载一次
- SPA应用程序只需要在第一次请求时加载页面,页面切换不需重新加载,而传统的Web应用程序必须在每次请求时都得加载页面,需要花费更多时间。因此,SPA页面加载速度要比传统 Web 应用程序更快。
- 更好的用户体验
- SPA 提供类似于桌面或移动应用程序的体验。用户切换页面不必重新加载新页面
- 切换页面只是内容发生了变化,页面并没有重新加载,从而使体验变得更加流畅
- 可轻松的构建功能丰富的Web应用程序
SPA的缺点
- SPA应用默认只返回一个空HTML页面,不利于
SEO
(search engine optimization ) - 首屏加载的资源过大时,一样会影响
首屏的渲染
- 也不利于构建复杂的项目,复杂 Web 应用程序的
大文件可能变得难以维护
1.2 SEO的优化
1.3 静态站点生成(SSG)
静态站点生成(SSG) 全称是:Static Site Generate,是预先生成好的静态网站
- SSG 应用一般在构建阶段就确定了网站的内容。
- 如果网站的内容需要更新了,那必须得重新再次构建和部署
- 构建 SSG 应用常见的库和框架有: Vue Nuxt、 React Next.js 等
SSG的优点
- 访问速度非常快,因为每个页面都是在构建阶段就已经提前生成好了。
- 直接给浏览器返回静态的HTML,也有利于SEO
- SSG应用依然保留了SPA应用的特性,比如:前端路由、响应式数据、虚拟DOM等
SSG的缺点
- 页面都是静态,不利于展示实时性的内容,实时性的更适合SSR。
- 如果站点内容更新了,那必须得重新再次构建和部署
1.4 SSR 服务器端渲染
服务器端渲染全称是:Server Side Render,在服务器端渲染页面,并将渲染好HTML返回给浏览器呈现
SSR应用的页面是在服务端渲染的,用户每请求一个SSR页面都会先在服务端进行渲染,然后将渲染好的页面,返回给浏览器呈现
优缺点
- 更快的首屏渲染速度
- 更好的SDO
- SSR应用程序在
Hydration
之后依然可以保留Web 应用程序的交互性
。比如:前端路由、响应式数据、虚拟DOM等。
- SSR 通常需要对服务器进行更多 API 调用,以及在服务器端渲染需要消耗更多的服务器资源,成本高
- 增加了一定的开发成本,用户需要关心哪些代码是运行在服务器端,哪些代码是运行在浏览器端
- SSR 配置站点的缓存通常会比SPA站点要复杂一点
1.5 SSR的解决方案
- php、jsp…………
- 从0搭建SSR项目
- 直接使用流行的框架
1.6 跨请求状态污染
在SPA中,整个生命周期中只有一个App对象实例 或 一个Router对象实例 或 一个Store对象实例都是可以的,因为每个用户在使用浏览器访问SPA应用时,应用模块都会重新初始化,这也是一种
单例模式
然而,在 SSR 环境下,
App应用模块通常只在服务器启动时初始化一次
。同一个应用模块会在多个服务器请求之间被复用,而我们的单例状态对象也一样,也会在多个请求之间被复用,比如:当某个用户对共享的单例状态进行修改,那么这个状态可能会意外地泄露给另一个在请求的用户这种情况称之为
跨请求状态污染
- 可以在每个请求中为整个应用
创建一个全新的实例
,包括后面的 router 和全局 store等实例。 - 所以我们在创建App 或 路由 或 Store对象时都是使用一个函数来创建,保证每个请求都会创建一个全新的实例
- 这样也会有缺点:需要消耗更多的服务器的资源
2.nuxt
2.1 nuxt定义
Nuxt 使用 h3来实现部署可移植性
Nuxt 框架可以用来快速构建下一个 Vue.js 应用程序,如支持 CSR 、SSR、SSG 渲染模式的应用等
特点
- Nuxt3 是基于 Vue3 + Vue Router + Vite 等技术栈,全程 Vue3+Vite 开发体验(Fast)。
- 自动导包
- Nuxt 会自动导入辅助函数、组合 API和 Vue API ,无需手动导入。
- 基于规范的目录结构,Nuxt 还可以对自己的组件、 插件使用自动导入
- 约定式路由(目录结构即路由)
- Nuxt 路由基于vue-router,在 pages/ 目录中创建的每个页面,都会根据目录结构和文件名来自动生成路由
- 渲染模式:Nuxt 支持多种渲染模式(SSR、CSR、SSG等)
- 通用渲染(服务器端渲染和水合)
- 仅客户端渲染
- 全静态站点生成
- 混合渲染(每条路由缓存策略)
- 利于搜索引擎优化:服务器端渲染模式,不但可以提高首屏渲染速度,还利于SEO
- 服务器引擎
- 在开发环境中,它使用 Rollup 和 Node.js
- 在生产环境中,使用 Nitro 将您的应用程序和服务器构建到一个通用.output目录中
- Nitro服务引擎提供了跨平台部署的支持,包括 Node、Deno、Serverless、Workers等平台上部署
2.2 环境搭建
命令行
- npx nuxi init hello-nuxt
- pnpm dlx nuxi init hello-nuxt
- npm install –g nuxi && nuxi init hello-nuxt
pnpm install --shamefully-hoist(创建一个扁平的 node_modules 目录结构,类似npm 和 yarn)
或者创建一个 .npmrc 文件,里面写入 shamefully-hoist=true
2.3 安装nuxt报错
-
配置hosts
- Mac电脑:/etc/hosts
- win电脑:c:/Windows/System32/drivers/etc/hosts
-
拖动到vscode里面去,追加下面这行
185.199.108.133 raw.githubusercontent.com
-
保存(以管理员身份覆盖)
185.199.108.133 raw.githubusercontent.com
2.4 package.json script
"build": "nuxt build"
:用于构建 Nuxt.js 应用程序。构建的过程中,Nuxt.js 会生成静态文件和服务器代码。"dev": "nuxt dev"
:用于在本地开发环境中启动 Nuxt.js 应用程序。该命令会启动一个本地服务器,并监听文件更改,以便在保存文件时自动重新加载您的应用程序。"generate": "nuxt generate"
:用于生成静态网站。该命令将预渲染所有页面,并生成一组静态 HTML 文件和关联的 JS 和 CSS 文件。这些文件可以部署到任何支持静态文件的 Web 服务器上。"preview": "nuxt preview"
:用于在本地启动预览服务器,预览静态生成的应用程序。预览服务器支持实时重载,并提供与生产环境相同的功能。您可以使用该命令来测试静态生成的应用程序,并查看实际生成的结果。"postinstall": "nuxt prepare"
:用于安装依赖项和设置应用程序。在应用程序安装时,该命令将安装所有必需的依赖项,并运行一些必要的设置步骤,以确保应用程序可以正常工作。会生成 .nuxt 目录
注意
postinstall
是 npm 的脚本钩子
prepublishOnly
: 在使用npm publish
发布包之前运行。如果脚本返回非零状态,则发布流程将被取消。prepare
: 在包安装时运行,以及在本地开发环境中运行npm link
和npm install
时运行。prepublish
: 在使用npm publish
发布包之前运行。该脚本也会在npm pack
打包时运行。preinstall
: 在包安装时运行,但在包下载之前运行。如果脚本返回非零状态,则安装流程将被取消。postinstall
: 在包安装后运行。通常用于安装依赖项并进行构建、编译或其他设置。preuninstall
: 在包卸载之前运行。如果脚本返回非零状态,则卸载流程将被取消。postuninstall
: 在包卸载后运行。通常用于清理文件或数据。preversion
: 在运行npm version
之前运行。如果脚本返回非零状态,则npm version
流程将被取消。version
: 在运行npm version
之后运行。通常用于在版本控制中更新文件或提交更改。postversion
: 在运行npm version
之后运行。通常用于向 Git 或其他存储库提交版本标记。pretest
: 在运行npm test
之前运行。如果脚本返回非零状态,则npm test
流程将被取消。test
: 运行npm test
命令时运行。posttest
: 在运行npm test
之后运行。prestart
: 在运行npm start
之前运行。如果脚本返回非零状态,则npm start
流程将被取消。start
: 运行npm start
命令时运行。poststart
: 在运行npm start
之后运行。prestop
: 在运行npm stop
之前运行。如果脚本返回非零状态,则npm stop
流程将被取消。stop
: 运行npm stop
命令时运行。poststop
: 在运行npm stop
之后运行。
2.5 目录结构
2.6 nuxt.config
nuxt.config.ts 配置文件位于项目的根目录,可对Nuxt进行自定义配置。比如,可以进行如下配置:
- runtimeConfig:运行时配置,即定义环境变量
- 可通过.env文件中的环境变量来覆盖,优先级(.env > runtimeConfig)
- .env的变量会打入到process.env中,符合规则的会覆盖runtimeConfig的变量
- .env一般用于某些终端启动应用时动态指定配置,同时支持dev和pro
- appConfig: 应用配置,定义在构建时确定的公共变量,如:theme
- 配置会和 app.config.ts 的配置合并(优先级 app.config.ts > appConfig)
- app:app配置
- head:给每个页面上设置head信息,也支持 useHead 配置和内置组件
- ssr:指定应用渲染模式
- router:配置路由相关的信息,比如在客户端渲染可以配置hash路由
- alias:路径的别名,默认已配好
- modules:配置Nuxt扩展的模块,比如:@pinia/nuxt @nuxt/image
- routeRules:定义路由规则,可更改路由的渲染模式或分配基于路由缓存策略(公测阶段)
- builder:可指定用 vite 还是 webpack来构建应用,默认是vite。如切换为 webpack 还需要安装额外的依赖
2.6.1 runtimeConfig
export default defineNuxtConfig({
devtools: { enabled: true },
runtimeConfig: {
// 放在 public的 keyvalue 可以在服务端访问,也可以在客户端访问
// 没有放在 public 和app 里面的只能在服务端访问
// 放在 app 的keyvalue 可以在 服务端访问,也可以在客户端访问
// 运行时的配置
appKey: "appKey",
public: {
baseUrl: "www.baidu.com",
},
app: {
name: "app",
},
},
});
# NUXT_ 开头
# 这里的环境变量会自动注入到 process.env 里面去(但是并不是有提示,在服务端能打印出来,在客户端不行)
# 这里定义的 如果和 runtimeConfig 重名,会进行覆盖(前提是 NUXT 开头)
NUXT_APP_KEY=env里面定义
NUXT_PUBLIC_AUTHOR=呆呆狗
AGE=30
NUXT_APP_KEY_AGE=40
env 文件的环境变量,可以在服务端用
process.env.xxxx
打印出来,但是客户端不行
const runtimeConfig = useRuntimeConfig();
console.log(
"runtimeConfig",
runtimeConfig,
process.env.NUXT_APP_KEY,
process.env.AGE
);
// console.log("process", process, process.env.NUXT_APP_KEY_AGE);
// 在客户端,process 包含了下面这些
// {
// "dev": true,
// "test": false,
// "env": {
// "NODE_ENV": "development"
// },
// "server": false,
// "client": true,
// "browser": true,
// "nitro": false,
// "prerender": false
// }
// console.log("runtimeConfig", runtimeConfig);
// if (process.server) {
// console.log("运行在 server");
// console.log(runtimeConfig.appKey, "runtimeConfig.appKey");
// }
// if (process.client) {
// console.log("运行在 client");
// console.log(runtimeConfig.appKey, "runtimeConfig.appKey"); // undefined
// }
2.6.2 app.config
Nuxt 3 提供了一个 app.config.ts 应用配置文件,用来定义在构建时确定的公共变量.比如:网站的标题、主题色 以及任何不敏感的项目配置
app.config.ts 配置文件中的选项不能使用env环境变量来覆盖,与 runtimeConfig 不同
不要将秘密或敏感信息放在 app.config.ts 文件中,该文件是客户端公开
// nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
// 定义应用的配置
// 在客户端和服务端都可以用
appConfig: {
title: "nuxt.config.ts 呆呆狗测试 nuxt3",
},
});
// app.vue
let appConfig = useAppConfig();
console.log("appConfig", appConfig.title, appConfig.age); // appConfig 我是单独的文件配置的 我是单独文件配置的age
// app.config.ts
export default defineAppConfig({
// 这里的优先级,大于 nuxt.config.js 中的配置
title: "我是单独的文件配置的",
age: "我是单独文件配置的age",
});
2.6.3 runtimeConfig 与 app.config 对比
runtimeConfig 和 app.config都用于向应用程序公开变量。要确定是否应该使用其中一种,以下是一些指导原则
runtimeConfig:定义环境变量,比如:运行时需要指定的私有或公共token
app.config:定义公共变量,比如:在构建时确定的公共token、网站配置
app.config 是不能再运行时更改的,而runtimeConfig 是可以改
2.6.4 SEO
- 在 nuxt.config.ts 中配置
- 在vue 文件中使用 useHead 函数
- 在vue文件 template 使用SEO 内置组件
// nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true },
// app 属性
app: {
head: {
title: "呆呆狗网站",
charset: "UFT-8",
meta: [
{ name: "keywords", content: "关键词1 关键词2" },
{ name: "description", content: "这是一个网站,描述" },
],
style: [
{
children: `body{ color: red; }`,
},
],
script: [
{
src: "http://daidaigou.com",
},
{
src: "http://daidaigoubody.com",
},
],
},
},
});
useHead({
script: [{ src: "www.daidaigou.com", body: true }],
});
<template>
<div>
<h2>测试</h2>
<Meta name="description" content="我是在template 里面添加的"></Meta>
<Style type="text/css" children="body { background-color: green; }"></Style>
<!-- <NuxtWelcome /> -->
</div>
</template>
2.6.5 样式
- vue 文件中 创建 style 标签书写
- 在 nuxt.config.ts 引入全局样式文件
- 在 assets 创建 css /scss(
如果用scss需要安装一下 npm i sass
) ,在vue 文件中引入也是可以的
<style scoped lang="scss">
@important "~/assets/styles/variables.scss";
/*
也可以这样导入,这样导入可以起一个别名,如果起了别名用的时候就是, abc.xxx,就是加了一个命名空间
as * 可以省略命名空间
@use '~/assets/styles/variables.scss' as abc
@use '~/assets/styles/variables.scss' as *
*/
</style>
export default defineNuxtConfig({
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "@/assets/_colors.scss" as *;'
}
}
}
}
})
// 给 SCSS 模块自动添加这个 样式文件
2.6.6 资源的导入
<template>
<div>
<h2 class="global_title">路由 首页</h2>
<img src="~/assets/images/user.png" alt="" />
<div class="bg"></div>
<img :src="bgImg" alt="" />
</div>
</template>
<script setup lang="ts">
import bgImg from "@/assets/images/feel.png";
</script>
<style scoped>
.bg {
width: 200px;
height: 200px;
background-image: url(assets/images/feel.png);
}
</style>
2.6.7 界面的创建
- Nuxt项目中的页面是在 pages目录 下创建的
- 在pages目录创建的页面,Nuxt会根据该页面的目录结构和其文件名来自动生成对应的路由。
- 页面路由也称为文件系统路由器(file system router),路由是Nuxt的核心功能之一
新建界面步骤
- 创建页面文件,比如: pages/index.vue
- 将< NuxtPage /> 内置组件添加到 app.vue
- 页面如果使用scss那么需要安装:npm i sass -D
命令快速创建界面
- npx nuxi add page home # 创建home页面
- npx nuxi add page detail/[id] # 创建detail页面
- npx nuxi add page user-[role]/[id] # 创建user页面
- npx nuxi add page login/index.vue 创建login 目录
2.6.8 组件导航
< NuxtLink>是Nuxt内置组件,是对 RouterLink 的封装,用来实现页面的导航
该组件底层是一个 < a >标签,因此使用 a + href 属性也支持路由导航
但是用a标签导航会有触发浏览器默认刷新事件,而 NuxtLink 不会,NuxtLink还扩展了其它的属性和功能
应用Hydration后(已激活,可交互),页面导航会通过前端路由来实现。这可以防止整页刷新
具体参考 官网
2.6.9 编程导航
Nuxt3除了可以通过< NuxtLink>内置组件来实现导航,同时也支持编程导航:
navigateTo
navigateTo 函数在服务器端和客户端都可用,也可以在插件、中间件中使用,也可以直接调用以执行页面导航,
navigateTo( to , options)
to: 可以是纯字符串 或 外部URL 或 路由对象,
options: 导航配置,可选
replace:默认为false,为true时会替换当前路由页面
external:默认为false,不允许导航到外部连接,true则允许
等一些其他
navigateTo 返回值是一个promise
仍然可以继续使用vue-router 的语法,但是建议使用
navigateTo
进行跳转
2.6.10 动态路由
动态路由也是根据目录结构和文件的名称自动生成
-
pages/detail/[id].vue -> /detail/:id
-
pages/detail/user-[id].vue -> /detail/user-:id
-
pages/detail/[role]/[id].vue -> /detail/:role/:id
-
pages/detail-[role]/[id].vue -> /detail-:role/:id
动态路由 和 index.vue 能同时存在, Next.js则可以
2.6.11 404
通过在方括号内添加三个点 ,如:[…slug].vue 语法,其中slug可以是其它字符串
除了支持在 pages根目录下创建,也支持在其子目录中创建
2.6.12 嵌套路由
- 创建一个一级路由,如:parent.vue,在page 根目录下
- 创建一个与一级路由同名同级的文件夹,如: parent
- 在parent文件夹下,创建一个嵌套的二级路由
- 如:parent/child.vue, 则为一个二级路由页面
- 如: parent/index.vue 则为二级路由默认的页面
- 需要在parent.vue中添加 NuxtPage 路由占位
2.6.13 中间件
执行的顺序是
- 全局中间件
- 界面自身中间件
2.6.14 路由验证
<template>
<div>
用户
{{ $route.params.id }}
</div>
</template>
<script lang="ts" setup>
definePageMeta({
validate: async (route) => {
console.log("进行验证~~~~");
// return /^\d+$/.test(route.params.id as string);
// return true; // true 成功
return {
// 如果返回对象,那也是验证失败
statusCode: 301,
statusMessage: "界面加载失败~",
};
},
});
</script>
<style scoped></style>
路由验证失败,可以自定义错误页面
在项目根目录(不是pages目录)新建
error.vue
<template>
<div>
可以自己定义错误的界面,error.vue error
<br />
{{ error }}
<br />
<button @click="goHome">返回首页</button>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
error: Object,
});
function goHome() {
navigateTo("/");
}
</script>
<style scoped></style>
2.6.15 layout 布局
- Layout布局是页面的包装器,可以将多个页面共性东西抽取到 Layout布局 中
- Layout布局是使用< slot >组件来显示页面中的内容
- Layout布局有两种使用方式
- 方式一:默认布局
- 在layouts目录下新建默认的布局组件,比如:layouts/default.vue
- 然后在app.vue中通过< NuxtLayout >内置组件来使用
- 方式二:自定义布局(Custom Layout)
- 继续在layouts文件夹下新建 Layout 布局组件,比如: layouts/custom-layout.vue
- 然后在app.vue中给内置组件 指定name属性 的值为:custom-layout
- 方式一:默认布局
如果,在某些界面上需要展示,这个 布局组件的头部,有时候不需要展示,有下面这几个方案
- 可以在布局组件,判断路由,通过路由确定是否要展示
- 封装hooks
- pinia
2.6.16 plugins 插件
创建插件的方式
- 方式一:直接使用 useNuxtApp() 中的 provide(name, vlaue) 方法直接创建,比如:可在App.vue中创建
- useNuxtApp 提供了访问 Nuxt 共享运行时上下文的方法和属性(两端可用):provide、hooks、callhook、vueApp等
- 方式二:在 plugins 目录中创建插件(推荐)
- 顶级和子目录index文件写的插件会在创建Vue应用程序时会自动加载和注册
- .server 或 .client 后缀名插件,可区分服务器端或客户端,用时需区分环境
- 在 plugins 目录下创建 plugins/price.ts 插件
- 接着 defineNuxtPlugin 函数创建插件,参数是一个回调函数
- 然后在组件中使用 useNuxtApp() 来拿到插件中的方法
插件注册顺序可以通过在文件名前加上一个数字来控制插件注册的顺序
plugins/1.price.ts 、plugins/2.string.ts、plugins/3.date.ts
export default defineNuxtPlugin((nuxt) => {
return {
provide: {
// 创建vue 实例的时候,就会创建好
formatPrice(price: number): string {
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
});
return formatter.format(price);
},
},
};
});
const nuxtApp = useNuxtApp();
nuxtApp.provide("formatData", () => {
return "2024-01-07";
});
console.log(nuxtApp.$formatData(), "是哦那个插件");
console.log(nuxtApp.$formatPrice(23), "plugins目录的擦火箭");
2.6.17 App 生命周期
App Hooks 主要可由 Nuxt 插件 使用 hooks 来监听 生命周期,也可用于 Vue 组合 API
但是,如在组件中编写hooks来监听,那 create和setup hooks就监听不了,因为这些hooks已经触发完了监听
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook("app:created", () => {
console.log("The app is mounted!");
// 执行一些操作...
});
});
2.6.18 获取数据
$fetch
:使用 $fetch 发起请求;这个方法会在 client 和 server 都会请求useAsyncData
:在刷新界面时,会减少客户端发起一次网络请求,但是切换路由啥的,还是会发;会阻塞页面导航
useFetch
:就是 useAsyncData 的简写形式;会阻塞页面导航
- 可以通过 useFetch 第二个参数 options 的 lazy 配置,value为true 则不会阻塞
- 这个函数会返回 好多东西;useFetch · Nuxt Composables ,比如 refresh 可以刷新请求,调一下即可刷新,还有 pending,一开始为false ,网络发起的时候改成true ,结束改成 false
- 如果 useFetch 的 options 请求参数 有 响应式数据,那么改成这个数据,也会自动发起请求
useLazyAsyncData
:就是把 lazy 设置为了 true,不会阻塞界面导航
,但是不确定接口返回值什么时候拿到,可以使用watch 监听- 这些函数只能用在 setup or Lifecycle Hooks 中
//1、$fetch:使用 $fetch 发起请求;这个方法会在 client 和 server 都会请求
$fetch(BASE_URL + "/homeInfo", {
method: "get",
}).then(
(res) => {
console.log("$fetch", res);
},
(err) => {}
);
// 2、useAsyncData:在刷新界面时,会减少客户端发起一次网络请求,但是切换路由啥的,还是会发
const { data } = await useAsyncData("loginInfo", () => {
return $fetch(BASE_URL + "/homeInfo");
});
console.log("useAsyncData", data);
// 3、useAsyncData的简写 useFetch
const { data } = await useFetch(BASE_URL + "/homeInfo");
console.log("useFetch", data);
// 4、useFetch get 请求 ,options 的设置
const { data } = await useFetch("/homeInfo", {
baseURL: BASE_URL,
query: {
name: "liujun",
},
// body: {}
// headers: {}
});
console.log("useFetch", data);
// 5、useFetch post 请求
const { data } = await useFetch("/goods", {
method: "post",
baseURL: BASE_URL,
body: {
count: 2,
},
// headers: {}
});
console.log("useFetch", data);
// 6、拦截器
// const { data } = await useFetch("/goods", {
// method: "POST",
// baseURL: BASE_URL,
// body: {
// count: 1,
// },
// // 请求的拦截 ( server and client )
// onRequest({ request, options }) {
// // console.log(options);
// // options.headers = {
// // token: "xxxx",
// // };
// },
// onRequestError({ request, options, error }) {
// console.log("onRequestError");
// },
// onResponse({ request, response, options }) {
// console.log("onResponse");
// console.log(response._data.data.server_jsonstr);
// response._data.data = {
// name: "liujun",
// };
// // return response._data.data.server_jsonstr;
// },
// onResponseError({ request, response, options, error }) {
// console.log("onResponseError");
// },
// });
// console.log("拦截器", data.value);
// 7、useFetch 会阻导航内容的渲染
// const { data } = await useFetch("/homeInfo", {
// baseURL: BASE_URL,
// lazy:true,// 不会阻塞,可以用 useLazyFetch 也是一样的
// query: {
// name: "liujun",
// },
// // body: {}
// // headers: {}
// });
2.6.19 server api
Nuxt3 提供了编写后端服务接口的功能,编写服务接口可以在server/api目录下编写
- 比如:编写一个 /api/homeinfo 接口
- 1.在server/api目录下新建 homeinfo.ts
- 2.接在在该文件中使用 defineEventHandler 函数来定义接口(支持 async)
- 3.然后就可以用useFetch函数轻松调用: /api/homeinfo 接口了
export default defineEventHandler((e) => {
// let { req, res } = e.node
// const query = getQuery(e)
return {
code: 200,
data: {
name: "liujun",
age: 20,
},
};
});
// 浏览器中,http://localhost:3000/api/homeInfo
// 如果这个接口是 get 请求,可以在创建文件的时候 xxx.get.ts 也可以
// https://www.nuxt.com.cn/docs/guide/directory-structure/server
{
"code": 200,
"data": {
"name": "liujun",
"age": 20
}
}
2.6.20 全局状态共享 useState
Nuxt跨页面、跨组件全局状态共享可使用 useState(支持Server和Client )
useState<T>(init?: () => T | Ref<T>): Ref<T>
useState<T>(key: string, init?: () => T | Ref<T>): Ref<T>
key
: 一个唯一的键,确保数据获取在请求中被正确地去重。如果你不提供键,则会为useState
的实例生成一个在文件和行号上唯一的键。init
: 当未初始化时,提供状态的初始值的函数。这个函数也可以返回一个Ref
。T
: (仅 TypeScript)指定状态的类型
useState 具体使用步骤如下
- 在 composables 目录下创建一个模块,如: composables/states.ts
- 在states.ts中使用 useState 定义需全局共享状态,并导出
- 在组件中导入 states.ts 导出的全局状态
useState 只能用在 setup 函数 和 Lifecycle Hooks 中
useState 不支持classes, functions or symbols类型,因为这些类型不支持序列化
// 创建一个响应式状态并设置默认值
export default function useCount() {
return useState("counter", () => 23); // 返回值是 ref
}
const res = useCount();
const addCount = () => {
res.value++;
};
2.6.21 pinia
npm install @pinia/nuxt –-save # @pinia/nuxt 会处理state同步问题,比如不需要关心序列化或XSS 攻击等问题
npm install pinia –-save # 如有遇到pinia安装失败,可以添加 --legacy-peer-deps 告诉 NPM 忽略对等依赖并继续安装。或使用yarn
import { defineStore } from "pinia";
export interface IState {
counter: number;
homeInfo: any;
}
export const useHomeStore = defineStore("home", {
state: (): IState => {
return {
counter: 0,
homeInfo: {},
};
},
actions: {
increment() {
this.counter++;
},
async fetchHomeData() {
const url = "http://codercba.com:9060/juanpi/api/homeInfo";
const { data } = await useFetch<any>(url);
console.log(data.value.data);
this.homeInfo = data.value.data;
},
},
});