前言
Nuxt3 的对比之前的2 和 1 ,只能感叹前端发展的越来越快了,不学无术
开发更快
打包更小
支持 vite
支持 vue3
支持自动引入
支持文件路由
支持布局系统
支持多种渲染模式
支持 typescript
支持 composition-api
安装NUXT3 需要node 大于16的版本
brew 更新 node
brew update
brew upgrade node
还有就是 node 安装
npm install -g n
npm install -g
sudo n stable
最后查看node版本

然后 安装官网的来
npx nuxi@latest init <project-name>
因为家里的网 没有那么快,需要在hosts 配置
sudo vi /etc/hosts

把上面的代码输入上去 按 : wq 退出保存
然后在运行

最后 安装 cd my-nuxt. npm run dev
打开目录 vscode

vetur 把插件卸载 然后装上 Volar. vue3 的插件 这个是有好
关于NUXT目录的
Nuxt 使用.nuxt/目录在开发中生成您的Vue应用程序。
你不应该碰里面的任何文件,因为整个目录将在运行 nuxt dev 时重新创建。
就是最上面的nuxt 目录
components 组件
组件名策略
默认情况下,Nuxt自动导入components目录中的任何组件,组件名将基于它的路径、目录和文件名。
惰性加载组件
要动态导入一个组件(也称为惰性加载组件),则在组件名称前添加 Lazy 前缀。通过使用 Lazy 前缀,你可以延迟加载组件代码,直到合适的时刻
<template>
<div>
<MyImg />
<LazyMyImg />
</div>
</template>
composables
composables 目录下的内容也将自动将 Vue 组合导入到应用中,Nuxt 只扫描 composables/ 目录的顶层文件。Composables 的主要作用是将常用逻辑和逻辑相关的代码抽象出来,以提高代码可复用性和可维护性,如:跨组件创建响应性的、对ssr友好的共享状态—— useState。
/** * composables/counter.ts 内容 **/ export const userCounter = () => { return useState('counter', () => 0); };
/** * 业务组件 **/ <script setup> import Count from "~/components/business/Count.vue"; const counter = userCounter(); </script> <template> <div> 业务组件内容: {{ counter }} <a-button type="primary" @click="counter--"> - </a-button> <a-button type="primary" @click="counter++"> + </a-button> </div> </template>
/** * business/Count 组件内容 **/ <template> <div>Count组件内容:{{ counter }}</div> </template> <script setup lang="ts"> const counter = userCounter(); </script>
layouts
Nuxt提供了一个可定制的布局框架,可以在整个应用程序中使用,非常适合将常见的UI或代码模式提取到可重用的布局组件中。布局放在layouts/目录中,使用时将通过异步导入自动加载。
默认布局
在layouts目录下添加default.vue 布局文件。
不像其他组件,布局组件必须有一个根元素,以允许 Nuxt 在布局变化之间应用过渡-这个根元素不能是<slot />。如果你的应用只有一个布局,建议使用app.vue。
在布局文件中,布局的内容将加载在<slot />中,~/layouts/default.vue:
<template>
<div>
<slot />
</div>
</template>
如果你使用app.vue你还需要添加 :
<template>
<NuxtLayout>
// 在app.vue中没有NuxtLayout组件,内容将会不显示
</NuxtLayout>
</template>
配置布局
-| layouts/ ---| default.vue ---| custom.vue
可以在 NuxtLayout 中添加 name 属性来覆盖默认布局:
<template>
<NuxtLayout :name="layout">
<NuxtPage />
</NuxtLayout>
</template>
<script setup>
// 您可以根据API调用或登录状态来选择此选项
const layout = "custom";
</script>
也可以通过 definePageMeta 设置
<template>
<NuxtLayout>
2023,12.23
</NuxtLayout>
</template>
<script setup>
definePageMeta({
layout: "custom",
});
</script>
如果业务组件不使用
<NuxtLayout>组件包裹,配置布局是不会生效的
方法
pages 页面
页面目录。Nuxt 提供了一个基于文件的路由,使用 Vue Router 在底层创建路由。pages/index.vue 文件将被映射到应用程序 / 路由。
如果你正在使用app.vue,确保在 app.vue 使用 <NuxtPage/> 组件来显示当前页面。
动态路由
建立页面文件时,如果命名时将任何内容放在方括号内,它将被转换为路由参数。在文件名或目录中混合和匹配多个参数。
-| 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>
nuxt3 中内置了四种请求的方法
useFetch
useLazyFetch
useAsyncData
useLazyAsyncData
推荐的话还是 useFetch
const param1 = ref('value1')
const { data, pending, error, refresh } = await useFetch('/api/modules', {
query: { param1, param2: 'value2' }
})
Request 的封装与使用
没有用 nuxt3 自带的接口返回格式才进行这样的简易封装;
- ~enums/interface.ts // 定义接口返回 code 值的枚举
export enum ResultEnum { SUCCESS = 0, TOKEN_OVERDUE = 404, // 用户登录失败 INTERNAL_SERVER_ERROR = 500, // 服务异常 }
- ~/composables/useDefaultRequest.ts // 定义接口统一拦截函数
import { UseFetchOptions } from "nuxt/app";
import { RouteLocationRaw } from "vue-router";
import { ResultEnum } from "~/enums/interface";
interface DefaultResult<T = any> {
code: number;
data: T;
msg: string;
success: boolean;
}
type UrlType = string | Request | Ref<string | Request> | (() => string | Request);
type HttpOption<T> = UseFetchOptions<DefaultResult<T>>;
interface RequestConfig<T = any> extends HttpOption<T> {
// 忽略拦截,不走拦截,拥有 responseData,且 code !== 0 的情况下,直接返回 responseData,
// 但是若无 responseData, 不设置 ignoreGlobalErrorMessage 也会报错
ignoreCatch?: boolean;
// 忽略全局错误提示,走拦截,但是任何情况下都不会提示错误信息
ignoreGlobalErrorMessage?: boolean;
}
const request = async <T>(
url: UrlType,
params: any,
options: RequestConfig<T>
): Promise<DefaultResult<T> | T> => {
const headers = useRequestHeaders(["cookie"]);
const method = ((options?.method || "GET") as string).toUpperCase();
const runtimeConfig = useRuntimeConfig();
const nuxtApp = useNuxtApp();
const { $message, $login } = nuxtApp;
const { apiBaseUrl } = runtimeConfig.public;
const baseURL = `${apiBaseUrl}/mall/api`;
// 处理用户信息过期
const hanlerTokenOverdue = async () => {
const { _route } = nuxtApp;
await $login(_route?.fullPath);
};
// 处理报错异常
const handlerError = (msg = "服务异常") => {
if (process.server) {
showError({ message: msg, statusCode: 500 });
} else {
$message.error(msg);
}
};
const { data, error } = await useFetch(url, {
baseURL,
headers,
credentials: "include",
params: method === "GET" ? params : undefined,
body: method === "POST" ? JSON.stringify(params) : undefined,
...options,
});
const responseData = data.value as DefaultResult<T>;
const { ignoreCatch, ignoreGlobalErrorMessage } = options; // 忽略全局
if (error.value || !responseData) {
if (!ignoreGlobalErrorMessage) handlerError();
return Promise.reject(error.value || "服务响应失败,请稍后重试");
} else {
const { code, data: result, msg } = responseData;
// 接口请求成功,直接返回结果
if (code === ResultEnum.SUCCESS || !code) {
return result;
}
if (!ignoreCatch) {
// 接口请求错误,统一处理
switch (code) {
case ResultEnum.TOKEN_OVERDUE: // 登录信息过期,去登录
// 用户信息过期
await hanlerTokenOverdue();
default:
if (!ignoreGlobalErrorMessage) handlerError(msg);
return Promise.reject(msg || "服务响应失败,请稍后重试");
}
}
}
return responseData;
};
// 自动导出
export const useDefaultRequest = { get: <T>(url: UrlType, params?: any, option?: RequestConfig<T>) => { return request<T>(url, params, { method: "get", ...option }); }, post: <T>(url: UrlType, params?: any, option?: RequestConfig<T>) => { return request<T>(url, params, { method: "post", ...option }); }, };
useFetch() 在 onMounted 执行返回值为 null?
onMounted(async () => { console.log('onMounted'); await nextTick(); testFetch1(); });
如果onMounted内需执行useFetch,应该放在await nextTick之后,否则会返回null;
在setup内(onMounted外)执行useFetch,服务端和客户端都执行,