单页应用程序 (SPA) 全称是:Single-page application,SPA应用是在客户端呈现的(术语称:
CSR
(Client Side Render))
SPA的优点
SPA的缺点
SEO
(search engine optimization )首屏的渲染
大文件可能变得难以维护
静态站点生成(SSG) 全称是:Static Site Generate,是预先生成好的静态网站
SSG的优点
SSG的缺点
服务器端渲染全称是:Server Side Render,在服务器端渲染页面,并将渲染好HTML返回给浏览器呈现
SSR应用的页面是在服务端渲染的,用户每请求一个SSR页面都会先在服务端进行渲染,然后将渲染好的页面,返回给浏览器呈现
优缺点
Hydration
之后依然可以保留 Web 应用程序的交互性
。比如:前端路由、响应式数据、虚拟DOM等。在SPA中,整个生命周期中只有一个App对象实例 或 一个Router对象实例 或 一个Store对象实例都是可以的,因为每个用户在使用浏览器访问SPA应用时,应用模块都会重新初始化,这也是一种
单例模式
然而,在 SSR 环境下,
App应用模块通常只在服务器启动时初始化一次
。同一个应用模块会在多个服务器请求之间被复用,而我们的单例状态对象也一样,也会在多个请求之间被复用,比如:当某个用户对共享的单例状态进行修改,那么这个状态可能会意外地泄露给另一个在请求的用户这种情况称之为
跨请求状态污染
创建一个全新的实例
,包括后面的 router 和全局 store等实例。Nuxt 使用 h3来实现部署可移植性
Nuxt 框架可以用来快速构建下一个 Vue.js 应用程序,如支持 CSR 、SSR、SSG 渲染模式的应用等
特点
命令行
pnpm install --shamefully-hoist(创建一个扁平的 node_modules 目录结构,类似npm 和 yarn)
或者创建一个 .npmrc 文件,里面写入 shamefully-hoist=true
配置hosts
拖动到vscode里面去,追加下面这行
185.199.108.133 raw.githubusercontent.com
保存(以管理员身份覆盖)
185.199.108.133 raw.githubusercontent.com
"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
之后运行。nuxt.config.ts 配置文件位于项目的根目录,可对Nuxt进行自定义配置。比如,可以进行如下配置:
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
// }
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",
});
runtimeConfig 和 app.config都用于向应用程序公开变量。要确定是否应该使用其中一种,以下是一些指导原则
runtimeConfig:定义环境变量,比如:运行时需要指定的私有或公共token
app.config:定义公共变量,比如:在构建时确定的公共token、网站配置
app.config 是不能再运行时更改的,而runtimeConfig 是可以改
// 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>
如果用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 模块自动添加这个 样式文件
<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>
新建界面步骤
命令快速创建界面
< NuxtLink>是Nuxt内置组件,是对 RouterLink 的封装,用来实现页面的导航
该组件底层是一个 < a >标签,因此使用 a + href 属性也支持路由导航
但是用a标签导航会有触发浏览器默认刷新事件,而 NuxtLink 不会,NuxtLink还扩展了其它的属性和功能
应用Hydration后(已激活,可交互),页面导航会通过前端路由来实现。这可以防止整页刷新
具体参考 官网
Nuxt3除了可以通过< NuxtLink>内置组件来实现导航,同时也支持编程导航:
navigateTo
navigateTo 函数在服务器端和客户端都可用,也可以在插件、中间件中使用,也可以直接调用以执行页面导航,
navigateTo( to , options)
to: 可以是纯字符串 或 外部URL 或 路由对象,
options: 导航配置,可选
replace:默认为false,为true时会替换当前路由页面
external:默认为false,不允许导航到外部连接,true则允许
等一些其他
navigateTo 返回值是一个promise
仍然可以继续使用vue-router 的语法,但是建议使用
navigateTo
进行跳转
动态路由也是根据目录结构和文件的名称自动生成
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则可以
通过在方括号内添加三个点 ,如:[…slug].vue 语法,其中slug可以是其它字符串
除了支持在 pages根目录下创建,也支持在其子目录中创建
执行的顺序是
- 全局中间件
- 界面自身中间件
<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>
如果,在某些界面上需要展示,这个 布局组件的头部,有时候不需要展示,有下面这几个方案
- 可以在布局组件,判断路由,通过路由确定是否要展示
- 封装hooks
- pinia
创建插件的方式
插件注册顺序可以通过在文件名前加上一个数字来控制插件注册的顺序
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目录的擦火箭");
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!");
// 执行一些操作...
});
});
$fetch
:使用 $fetch 发起请求;这个方法会在 client 和 server 都会请求useAsyncData
:在刷新界面时,会减少客户端发起一次网络请求,但是切换路由啥的,还是会发;会阻塞页面导航
useFetch
:就是 useAsyncData 的简写形式;会阻塞页面导航
useLazyAsyncData
:就是把 lazy 设置为了 true,不会阻塞界面导航
,但是不确定接口返回值什么时候拿到,可以使用watch 监听//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: {}
// });
Nuxt3 提供了编写后端服务接口的功能,编写服务接口可以在server/api目录下编写
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
}
}
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 具体使用步骤如下
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++;
};
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;
},
},
});