视频教程 www.bilibili.com/video/BV11W…
官网 nuxt.com.cn/
目录
.nuxt临时文件.output打包输出assets静态资源assets/目录用于添加构建工具将处理的所有网站资源。components组件目录 会自动注册为全局组件composables使用 composables/ 目录将您的 Vue 组合式函数自动导入到您的应用程序中。contentlayouts布局页面middleware中间件modulesnode_modules依赖pages路由plugins插件public公共资源 里面的文件不会进行打包,在访问的时候不需要添加public前缀server服务器utils在整个应用程序中自动导入您的实用程序函数.env.gitignore.nuxtignoreapp.vue入口文件 主页面app.config.tserror.vue错误处理页面nuxt.config.tsnuxt配置文件package.json安装依赖tsconfig.json
composables
使用 composables/ 目录将您的 Vue 组合式函数自动导入到您的应用程序中。
在这个目录导出的内容,在使用的时候不需要引入
在根目录创建composables目录,在目录下面创建myCom.js文件,代码如下
// 导出一个add函数
export const add = (x, y) => {
return x + y;
};
// 使用默认的方式
export default function (x, y) {
return x + y;
}
导出的方法可以直接使用,使用默认导出的方式是直接使用文件名
console.log('计算',add(1,2));
console.log('默认方式',myCom(1,2));
nuxt只会扫描一层目录,如果composables里面还有子目录,是不会自动导入的如果想要扫描多层,需要修改配置文件
地址 composables/ · Nuxt 目录结构 - Nuxt 中文 (nuxtjs.org.cn)
imports: { dirs: [ // Scan top-level modules 扫描顶层模块 "composables", // ... or scan modules nested one level deep with a specific name and file extension // 用特定名称和文件扩展名扫描嵌套在一级深处的模块 "composables/*/index.{ts,js,mjs,mts}", // ... or scan all modules within given directory // 扫描给定目录中的所有模块 "composables/**" ] },
utils使用方式和composables一样
$fetch和useAsyncData
Nuxt 使用 ofetch 在全局暴露 $fetch 助手,用于进行 HTTP 请求。
$fetch是使用Promise封装的
编写接口
在根目录的server目录下面创建api/myIp.js,代码如下
export default defineEventHandler(event => {
console.count('次数')
// 通过return 的方式进行返回数据 可以直接返回json对象
return {
code: 200,
ip: "171.95.220.50",
country: "中国",
region: "四川",
city: "南充",
asn: "AS4134",
isp: "电信",
latitude: "",
longitude: "",
LLC: "Chinanet"
};
});
也可以直接使用在线的公共接口地址 UApi-免费公益开放的API接口平台 (uapis.cn)
$fetch
在vue文件中使用
<!-- \pages\index.vue -->
<script setup lang="ts">
const res = await $fetch('/api/myIp')
console.log("🚀 ~ res:", res)
</script>
<template>
<div>首页</div>
</template>
查看控制台可以看出来是在服务端执行了一次,然后在客户端执行了一次,接口也执行了两次,如果是操作数据库会存在问题,需要只在客户端执行,因此需要修改一下
<!-- \pages\index.vue -->
<script setup lang="ts">
if (import.meta.client) {
const res = await $fetch("/api/myIp");
console.log("🚀 ~ res:", res);
}
</script>
<template>
<div>首页</div>
</template>
这样写法是不正确的,因此官方提供了useAsyncData
useAsyncData
将上面代码修改为如下,返回的数据是响应式数据。
<!-- \pages\index.vue -->
<script setup lang="ts">
// if (import.meta.client) {
// const res = await $fetch("/api/myIp");
// console.log("🚀 ~ res:", res);
// }
// 第一个参数是一个key 第二个参数是一个函数
const res = useAsyncData("test", () => {
return $fetch("/api/myIp");
});
console.log("🚀 ~ res ~ res:", res.data.value);
</script>
<template>
<div>首页</div>
</template>
此时在服务端数据的是null,在客户端是返回的数据,原因是useAsyncData也是异步的,需要在useAsyncData前面添加await,这样服务端和客户端都是打印出数据,接口只会执行一次
useAsyncData只会在服务端执行一次
通过事件调用
在页面中创建一个按钮,在按钮上添加一个事件,在事件中获取接口的数据
<!-- \pages\index.vue -->
<script setup lang="ts">
// if (import.meta.client) {
// const res = await $fetch("/api/myIp");
// console.log("🚀 ~ res:", res);
// }
// 第一个参数是一个key 第二个参数是一个函数
// const res =await useAsyncData("test", () => {
// return $fetch("/api/myIp");
// });
// console.log("🚀 ~ res ~ res:", res.data.value);
const test1 = async () => {
const res = await useAsyncData("test", () => {
console.log('打印');
return $fetch("/api/myIp");
});
console.log("🚀 ~ res ~ res:", res.data.value);
};
</script>
<template>
<div>首页</div> <br>
<el-button type="primary" size="default" @click="test1">test1</el-button>
</template>
这样就变成在客户端进行调用执行了,同样是执行了一次接口
useAsyncData在服务端只会执行一次
useAsyncData函数的key在客户端和服务端执行的期间需要是唯一的,如果key不一样,那么会调用两次
useFetch,lazy,refresh
useFetch
useFetch实际上是useAsyncData和$fetch的语法糖,返回数据和useAsyncData是一样的
useFetch自动添加了一些属性,例如:refresh、error、pending(已经废弃,使用status代替)
refresh
作用是重新发送一次请求,只会在服务端执行一次
一般用于刷新token
status
请求的状态
<!-- \pages\index.vue -->
<script setup lang="ts">
// 第一个参数是url
// 没有useAsyncData中的key 是自动维护的
const {refresh,status} = await useFetch('/api/myIp')
console.log("🚀 ~ res:",status.value)
// refresh()
const test1 = () => {}
</script>
<template>
<div>首页</div> <br>
<div v-if="status === 'pending'">
正在加载...
</div>
<div v-if="status === 'success'">
加载完成
</div>
<el-button type="primary" size="default" @click="test1">test1</el-button>
</template>
status是返回的success,原因是接口请求是异步的,只会返回结果,去掉await,会先返回pending,然后返回success,通过这个状态可以在页面显示过度效果
在服务端只会显示成功的状态,但是在客户端会有两个状态
- 服务端:刷新的时候、直接进入页面都是服务端,页面是服务端返回的
- 客户端:在其他页面跳转到当前页面,
status会显示默认值pending,成功之后就显示success
lazy
在useFetch前面添加await就无法展示过度效果了,如果想要两者都展示,那么就需要使用lazy
<!-- \pages\index.vue -->
<script setup lang="ts">
// 第一个参数是url
// 没有useAsyncData中的key 是自动维护的
const {refresh,status,data} = useFetch('/api/myIp',{
lazy:true
})
console.log("🚀 ~ res:",status.value,data.value)
// refresh()
const test1 = () => {}
</script>
<template>
<div>首页</div> <br>
<div v-if="status === 'pending'">
正在加载...
</div>
<div v-if="status === 'success'">
加载完成
</div>
<el-button type="primary" size="default" @click="test1">test1</el-button>
</template>
将lazy设置成true,就会先渲染标签
官方还提供了useLazyFetch的命令,可以实现上面的效果
<!-- \pages\index.vue -->
<script setup lang="ts">
// 第一个参数是url
// 没有useAsyncData中的key 是自动维护的
// const {refresh,status,data} = useFetch('/api/myIp',{
// lazy:true
// })
// console.log("🚀 ~ res:",status.value,data.value)
// refresh()
const {refresh,status,data} = await useLazyFetch('/api/myIp')
console.log("🚀 ~ res:",status.value,data.value)
const test1 = () => {}
</script>
<template>
<div>首页</div> <br>
<div v-if="status === 'pending'">
正在加载...
</div>
<div v-if="status === 'success'">
加载完成
</div>
<el-button type="primary" size="default" @click="test1">test1</el-button>
</template>
useFetch封装
为什么不用axios,因为他没有处理两次请求的问题
在composables中创建api.js进行封装useFetch
请求拦截器
export const apiCore = (url, opt) => {
// 获取全局变量
// 环境变量 nuxt.config.ts 文件的 runtimeConfig中定义
const config = useRuntimeConfig();
return useFetch(url, {
baseURL: config.public.baseUrl,
// 请求拦截器
onRequest: ({ options }) => {
// 获取token
let token = "";
// 只能在客户端获取token
if (import.meta.client) {
token = localStorage.getItem("token");
}
// 添加 headers
options.headers = {
// 添加认证
Auth: `token ${token}`,
// 原本的headers
...options.headers
};
},
...opt
});
};
// 封装get
export const getApi = (url, opt = {}) => {
// console.log("🚀 ~ getApi ~ url, opt:", url, opt);
// return apiCore(url, {
// method: "GET"
// });
return new Promise((resolve, reject) => {
return apiCore(url, {
method: "GET",
...opt
}).then(res => {
// 直接将真正的数据返回
resolve(res.data.value)
}).catch(err => {
reject(err)
})
});
};
在页面中使用
<!-- \pages\index.vue -->
<script setup lang="ts">
const test0 = async () => {
const res = await getApi("/uapi/api/weather?name=北京市");
// const res = await getApi('/api/myIp')
console.log("服务端请求:", res);
};
test0();
const test1 = async () => {
const res = await getApi("/uapi/api/say");
// const res = await getApi('/api/myIp')
console.log("随机一言:", res);
};
const test2 = async () => {
const res = await getApi("/uapi/api/weather?name=北京市");
// const res = await getApi('/api/myIp')
console.log("天气查询:", res);
};
const login = async () => {
localStorage.setItem("token", "123456789");
};
</script>
<template>
<div>首页</div>
<br />
<el-button type="primary" size="default" @click="test1">随机一言</el-button>
<el-button type="primary" size="default" @click="test2">天气查询</el-button>
<el-button type="primary" size="default" @click="login">模拟登录</el-button>
</template>
在客户端和服务端都可以正常运行
响应拦截器
在axios中 onResponse 是http状态码2XX之内的,onRequestError是http状态码2xx之外的,在 useFetch 中不会区分
onResponse和onRequestError的response参数里面会有一个ok属性 ,如果ok是true,就在onResponse中执行,如果是false,则两个都会执行
http的状态码是2xx,那么ok就是true,http的状态码是4xx或500,那么ok就是false
export const apiCore = (url, opt) => {
// 获取全局变量
// 环境变量 nuxt.config.ts 文件的 runtimeConfig中定义
const config = useRuntimeConfig();
const nuxtApp = useNuxtApp();
return useFetch(url, {
baseURL: config.public.baseUrl,
// 请求拦截器
onRequest: ({ options }) => {
// 获取token
let token = "";
// 只能在客户端获取token
if (import.meta.client) {
token = localStorage.getItem("token");
}
// 添加 headers
options.headers = {
// 添加认证
Auth: `token ${token}`,
// 原本的headers
...options.headers
};
},
/**
* 在axios中 onResponse 是http状态码2XX之内的,onRequestError是http状态码2xx之外的,在 useFetch 中不会区分
*/
// onResponse和onRequestError的response参数里面会有一个 ok 属性 ,如果ok是true,就在onResponse中执行,否则在onRequestError中执行
// 相应拦截器
onResponse({ response }) {
// http状态码是2xx 的情况
if (response.status >= 200 && response.status < 300) {
// 只有状态码是2xx的时候才能执行
// 根据后端返回的code值设置成功,例如 后端返回的code是200表示成功
// 这里的code值是后端返回的,上面的200是http的状态码
if (response._data.code !== 200) {
// 这里表示后端返回错误
// ElMessage.error(response._data.code + "" + response._data.message);
// elmessage 在服务端没有 因此需要添加客户端限制
if (import.meta.client) {
ElMessage.error(response._data.code + "" + response._data.message);
} else {
// 在服务端跳转到错误页面
console.log("服务端");
nuxtApp.runWithContext(() => {
// 注意 这个页面需要再导航守卫中放行才可以
navigateTo({
path: "/error",
query: {
code: response._data.code,
message: response._data.message
}
});
});
}
}
} else {
// http状态码不是2xx的情况
if (import.meta.client) {
ElMessage.error(response._data.code + "" + response._data.message);
} else {
// 在服务端跳转到错误页面
nuxtApp.runWithContext(() => {
// 注意 这个页面需要再导航守卫中放行才可以
navigateTo({
path: "/error",
query: {
code: response._data.code,
message: response._data.message
}
});
});
}
}
},
// 错误处理部分 代码没有生效
onRequestError({ response }) {
if (import.meta.client) {
ElMessage.error(response._data.code + "" + response._data.message);
} else {
// 在服务端跳转到错误页面
nuxtApp.runWithContext(() => {
// 注意 这个页面需要再导航守卫中放行才可以
navigateTo({
path: "/error",
query: {
code: response._data.code,
message: response._data.message
}
});
});
}
},
...opt
});
};
// 封装get
export const getApi = (url, opt = {}) => {
return new Promise((resolve, reject) => {
return apiCore(url, {
method: "GET",
...opt
})
.then(res => {
// 直接将真正的数据返回
resolve(res.data.value);
})
.catch(err => {
reject(err);
});
});
};
在视频教程中错误处理使用的是
onRequestError,但是在我的代码中没有执行,因此将处理方式放在了onResponse中
处理跨域
在文件nuxt.config.ts添加nitro
nitro: {
devProxy: {
// 被代理的路径
"/uapi": {
// 代理的目标地址
target: "https://uapis.cn",
// 表示在代理请求时更改源域名
changeOrigin: true,
// 表示在转发请求时,会在目标 URL 前添加原始请求的路径
prependPath: true,
},
},
},
状态处理
nuxt自带了一个状态处理是state
state
<!-- \pages\index.vue -->
<script setup lang="ts">
// 定义一个数据
let res = useState('num',() => {
return 0
})
// 会执行两次 分别是在服务端执行和客户端执行
res.value++
console.log("🚀 ~ res ~ res:", res.value)// 1 2
console.log('数据',useState('num').value)//1 2
</script>
<template>
<div>首页</div>
<br />
<el-button type="primary" size="default" >随机一言</el-button>
</template>
useState是定义一个数据,在服务端和客户端都会执行,可以通过俩个种方式获取到结果
创建的state在服务端和客户端都可以访问到,是共享的
在服务端创建的数据:在客户端和服务端都可以获取到
在客户端创建的数据:只能在客户端获取到数据,在服务端获取不到数据,因为直接在服务端获取数据相当于是刷新页面,刷新页面之后数据就丢失了
点击页面跳转还是能够获取到数据,因为这是客户端渲染
如果直接在地址栏输入地址,是后去不到数据的,因为这是服务端渲染,是会刷新页面的
pinia
使用pinia
安装pinia,nuxt.com.cn/modules/pin…
npm i pinia @pinia/nuxt
安装完成之后在nuxt.config.ts文件的modules进行引入
modules: ["@element-plus/nuxt",'@pinia/nuxt'],
接下来就可以使用了
在composables中创建一个userStore.js
import { defineStore } from "pinia";
// 第一个参数是名称 第二个参数是回调函数
export const userStore = defineStore("userStore", () => {
// 定义内容
const token = ref("");
// 设置token
const setToken = val => {
token.value = val;
};
// 获取token
const getToken = () => {
return token.value;
};
// 将数据和方法暴漏
return { token, setToken, getToken };
});
在页面中使用,上面导出的是一个函数
<!-- \pages\index.vue -->
<script setup lang="ts">
const login = () => {
userStore().setToken("123");
};
</script>
<template>
<div>首页</div>
<br />
<el-button type="primary" size="default" @click="login">设置token</el-button>
</template>
在刷新页面之后,pinia中的数据就丢失了
在服务端设置的数据,在客户端能够获取到;在客户端设置的数据,在服务端也是获取不到,和state一样
持久化
引入pinia持久化插件,nuxt.com.cn/modules/pin…
npm i -D @pinia-plugin-persistedstate/nuxt
安装之后进行引入
modules: [
"@element-plus/nuxt",
"@pinia/nuxt",
"@pinia-plugin-persistedstate/nuxt"
],
在教程中还需要在
nuxt.config.ts文件中配置buildbuild:{ transpile:["pinia-plugin-persistedstate"] },教程的
nuxt版本是3.12.3,学习时的版本是3.13.0
然后对userStore进行配置持久化
import { defineStore } from "pinia";
// 第一个参数是名称 第二个参数是回调函数
export const userStore = defineStore("userStore", () => {
// 定义内容
const token = ref("");
// 设置token
const setToken = val => {
token.value = val;
};
// 获取token
const getToken = () => {
return token.value;
};
// 将数据和方法暴漏
return { token, setToken, getToken };
},{
persist:{
key:"user", // 存储的名称 非必须
storage:persistedState.localStorage
}
});
这样就可以在客户端和服务端进行互通数据,但是在提示工具中是看不到的,需要在客户端打印一下才能看到,因为客户端去获取store,那么服务端的数据就会被同步过来,但是数据不是持久化的,之后在客户端修改之后才会进行持久化。
如果不打印可以在控制台输入
window.$pinia.state.value查看数据
即时设置的持久化插件也不能实现客户端设置数据,服务端获取数据,只能是服务端设置数据,客户端获取
保存到cookie实现客户端和服务端共享数据
如果将持久化数据保存到cookie中,在客户端设置数据,服务端是可以获取到的
持久化插件默认是保存到cookie中的
import { defineStore } from "pinia";
// 第一个参数是名称 第二个参数是回调函数
export const userStore = defineStore("userStore", () => {
// 定义内容
const token = ref("");
// 设置token
const setToken = val => {
token.value = val;
};
// 获取token
const getToken = () => {
return token.value;
};
// 将数据和方法暴漏
return { token, setToken, getToken };
},{
// persist:{
// key:"user", // 存储的名称 非必须
// storage:persistedState.localStorage
// }
persist:true
});
修改拦截器和导航守卫
修改导航守卫
修改token的获取
export default defineNuxtRouteMiddleware((to, from) => {
// 白名单
let passURL = ["/login","/about", "/error"];
if (!passURL.includes(to.path)) {
let token = "";
if (import.meta.client) {
// 客户端在pinia中获取token
token = userStore().getToken();
if (!token) {
return navigateTo({
path: "/login",
query: {
code: 401,
message: "请先登录"
}
});
}
}
}
});
修改拦截器
获取token同样替换
export const apiCore = (url, opt) => {
// 获取全局变量
// 环境变量 nuxt.config.ts 文件的 runtimeConfig中定义
const config = useRuntimeConfig();
const nuxtApp = useNuxtApp();
return useFetch(url, {
baseURL: config.public.baseUrl,
// 请求拦截器
onRequest: ({ options }) => {
// 获取token
let token = userStore().getToken();
// 添加 headers
options.headers = {
// 添加认证
Auth: `token=${token}`,
// 原本的headers
...options.headers
};
},
/**
* 在axios中 onResponse 是http状态码2XX之内的,onRequestError是http状态码2xx之外的,在 useFetch 中不会区分
*/
// onResponse和onRequestError的response参数里面会有一个 ok 属性 ,如果ok是true,就在onResponse中执行,否则在onRequestError中执行
// 相应拦截器
onResponse({ response }) {
console.log("🚀 ~ onResponse ~ response:", response);
// http状态码是2xx 的情况
if (response.status >= 200 && response.status < 300) {
// 只有状态码是2xx的时候才能执行
// 根据后端返回的code值设置成功,例如 后端返回的code是200表示成功
// 这里的code值是后端返回的,上面的200是http的状态码
if (response._data.code !== 200) {
// 这里表示后端返回错误
// ElMessage.error(response._data.code + "" + response._data.message);
// elmessage 在服务端没有 因此需要添加客户端限制
if (import.meta.client) {
ElMessage.error(response._data.code + "" + response._data.msg);
} else {
// 在服务端跳转到错误页面
console.log("服务端");
nuxtApp.runWithContext(() => {
// 注意 这个页面需要再导航守卫中放行才可以
navigateTo({
path: "/error",
query: {
code: response._data.code,
message: response._data.msg
}
});
});
}
}
}
// else {
// // http状态码不是2xx的情况
// if (import.meta.client) {
// ElMessage.error(response._data.code + "" + response._data.msg);
// } else {
// // 在服务端跳转到错误页面
// nuxtApp.runWithContext(() => {
// // 注意 这个页面需要再导航守卫中放行才可以
// navigateTo({
// path: "/error",
// query: {
// code: response._data.code,
// message: response._data.msg
// }
// });
// });
// }
// }
},
// 错误处理部分 代码没有生效
onRequestError({ response }) {
console.log("🚀 ~ onRequestError ~ response:", response);
if (import.meta.client) {
console.log("7777+++--+-");
ElMessage.error(response._data.code + "" + response._data.message);
} else {
console.log("888---");
// 在服务端跳转到错误页面
nuxtApp.runWithContext(() => {
// 注意 这个页面需要再导航守卫中放行才可以
navigateTo({
path: "/error",
query: {
code: response._data.code,
message: response._data.message
}
});
});
}
},
...opt
});
};
// 封装get
export const getApi = (url, opt = {}) => {
return new Promise((resolve, reject) => {
return apiCore(url, {
method: "GET",
...opt
})
.then(res => {
// 直接将真正的数据返回
resolve(res.data.value);
})
.catch(err => {
reject(err);
});
});
};
出现问题:在网络里面看不到请求,控制台也没有报错
原因:设置的
token是中文,有可能是编码的问题造成的,正常情况下token不可能是汉字
nuxt错误处理
nuxt提供了一个错误处理页面叫做error,在根目录下面创建error.vue文件
在error.vue页面,nuxt动态绑定了一个叫error的props属性
<script setup lang="ts">
defineProps({
// nuxt动态绑定了一个叫error的属性
error: Object
});
// 清除错误 并跳转到某个页面
clearError({redirect:"/login"})
</script>
<template>
<div>错误处理</div>
<!-- 错误码 -->
<div>{{ error.statusCode }}</div>
<!-- 错误信息 -->
<div>{{ error.message }}</div>
</template>
<style lang="scss"></style>
<!-- \pages\index.vue -->
<script setup lang="ts">
// 手动抛出错误 只能在服务端触发
// throw createError({ statusCode: 404, message: "自定义错误" });
// 客户端触发
const toError = () => {
showError({ statusCode: 404, message: "自定义错误" });
}
</script>
<template>
<div>首页</div>
<br />
<!-- <div>{{ aa.cc }}</div> -->
<el-button type="primary" size="default" @click="toError">错误</el-button>
</template>
seo优化
自定义标题是在页面中通过useHead定义
<script setup>
let title = ref('测试标题');
useHead({
title: "首页",
meta: [
{
name: "keywords",
content: "vue3,nuxt3,nuxt"
},
{
name: "description",
content: "vue3+nuxt3"
}
],
// 参数是当前显示的标题
// titleTemplate(titleChunk){
// console.log("🚀 ~ titleTemplate ~ titleChunk:", titleChunk)
// return titleChunk ? `${titleChunk} - vue3+nuxt3` : "vue3+nuxt3";
// }
// %s 是占位符 显示的是 titleChunk 数据
// titleTemplate:"%s 首页"
// titleTemplate:`%s ${title.value}`
});
</script>
如果app.vue中定义了,那么所有没有设置过的页面都会显示app.vue中的,查看源代码可以看到设置的meta
可以直接设置,也可以通过titleTemplate设置
layout
在根目录创建layouts文件夹,下面创建用于布局的页面
默认布局
创建default.vue文件,将app.vue中的布局代码挪过来
<script setup lang="ts">
console.log('默认布局');
</script>
<template>
<header>
<NuxtLink to="/">首页</NuxtLink>
<NuxtLink to="/about">关于我们</NuxtLink>
<NuxtLink to="/api">api</NuxtLink>
</header>
<main>
<!-- 需要使用插槽 -->
<slot></slot>
</main>
<footer>尾部</footer>
</template>
<style scoped lang="scss">
header,
footer,
main {
border: 1px solid #ccc;
}
</style>
app.vue文件代码
<script setup>
useHead({
title: "app",
meta: [
{
name: "keywords",
content: "vue3,nuxt3,nuxt"
},
{
name: "description",
content: "vue3+nuxt3"
}
]
});
</script>
<template>
<!-- 使用布局 -->
<!--
nuxt-layout标签就是 layouts/default.vue组件
-->
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
<style scoped lang="scss"></style>
不适用布局
如果不想使用布局,例如login页面,那么添加如下代码即可
definePageMeta({
layout: false
});
命名布局
在layouts文件夹,下面创建custom.vue
<script setup lang="ts">
console.log('默认布局');
</script>
<template>
<header>
<NuxtLink to="/">首页</NuxtLink>
<NuxtLink to="/about">关于我们</NuxtLink>
<NuxtLink to="/api">api</NuxtLink>
</header>
<main>
<!-- 需要使用插槽 -->
<slot></slot>
</main>
<footer>自定义布局</footer>
</template>
<style scoped lang="scss">
header,
footer,
main {
border: 1px solid #ccc;
}
</style>
在aboput页面使用上面的布局,layout: 'custom'设置布局样式,custom是文件名称
<script setup lang="ts">
definePageMeta({
path: "/about1",
layout: "custom"
});
</script>
<template>
<div>about</div>
</template>
<style lang="scss"></style>
或者在NuxtLayout标签上添加name属性,如果app.vue或父组组价中设置了layout,那么需要在definePageMeta中设置layout为false
<script setup lang="ts">
definePageMeta({
path: "/about1",
layout: false
});
</script>
<template>
<NuxtLayout name="custom">
<div>about</div>
</NuxtLayout>
</template>
<style lang="scss"></style>
打包发布
在文件nuxt.config.ts可以配置生产环境和开发环境的配置
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: "2024-04-03",
devtools: { enabled: true },
// 生产环境的配置
$production: {},
// 开发环境的配置
$development: {},
// ……
});
打包命令是npm run build,打包目录是.output目录,打包结束之后执行命令node .output/server/index.mjs就可以了
附录
vue和nuxt对照
| vue | Nuxt | 含义 |
|---|---|---|
router-view | NuxtPage | 路由入口 |
router-link | NuxtLink | 页面跳转 |
router.push | navigateTo | 编程式路由 |
| 路由 | 1. /pages目录 2.命名路由 3. 可选路由 4. 自定义路由 5. 嵌套路由 | 将vue的路由变成了多种方式,不需要在编写路由表,直接写在/pages中 |
| 动态路由 | 1. 命名路由 2. 可选路由 3. 全局路由 | 1. 命名路由:创建[id].vue文件,路径不能省略 2. 可选路由:创建[[id]]目录,在目录下创建对应的文件,路径中可以省略 3. 全局路由:相当于是404路由,当所有的路由匹配不到时执行<br / |
路由中的name | 自定义路由 | 在文件中编写,相当于是vue路由的name属性 |
| 路由守卫 | 中间件/middleware目录 | 在vue中需要写到路由表中,在nuxt中是在middleware目录下,在使用的地方进行引入,也可以直接在页面中编写;全局中间件是名称.global.js,不需要进行引入 |
axios | useFetch | nuxt提供的方法,可以设置请求拦截器、响应拦截器 |
vuex | state | 数据管理,state是nuxt自带的 |
pinia | pinia | 在vuex中数据一般是存到localStorage,在nuxt中推荐放在cookie中,因为这样客户端和服务端都可以设置或获取数据 |