1. 参考学习链接
https://github.com/codprincess/j-book-demo.git
2. 创建 demo 项目
npx nuxi init nuxt4-demo
✔ Which package manager would you like to use? pnpm
✔ Initialize git repository? No
✔ Would you like to install any of the official modules? No
3. 启动项目
cd nuxt4-demo
pnpm dev
4. 创建项目结构
在 app 目录下创建 pages 目录,增加 about.vue,home.vue 文件,此时修改 app.vue 文件
<template>
<div>
<!-- <NuxtRouteAnnouncer />
<NuxtWelcome /> -->
<NuxtPage />
</div>
</template>
此时查看 http://localhost:3001/ 找不到页面,因为没有配置路由,所以需要配置路由 page 目录下创建 index.vue 文件
5.配置 Ant design Vue
1. 1 安装依赖包
pnpm add -D @ant-design-vue/nuxt
在 nuxt.config.ts 中添加 ant-design-vue
export default defineNuxtConfig({
modules: ['@ant-design-vue/nuxt'],
})
1.2.按需配置
pnpm install unplugin-vue-components -D
// TODO 待完善 // TODO 主题配置待完善
2. 1 安装依赖包
pnpm add ant-design-vue
[plugin:vite:import-analysis] Failed to resolve import "ant-design-vue/es/button/style/css" from "app/pages/index.vue". Does the file exist?
解决方案 blog.csdn.net/a1308003218…
// https://nuxt.com/docs/api/configuration/nuxt-config
import Components from "unplugin-vue-components/vite";
import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
export default defineNuxtConfig({
compatibilityDate: "2025-07-15",
devtools: { enabled: true },
// modules: ['@ant-design-vue/nuxt'],
vite: {
plugins: [
// 按需引⼊组件
Components({
resolvers: [AntDesignVueResolver({ importStyle: false })],
}),
],
// ssr
ssr: {
noExternal: ["ant-design-vue"],
},
},
});
在需要组件中直接引入看效果
<template>
<!-- vue3页面 -->
<div>根节点页面 </div>
<a-button type="primary">按钮</a-button>
</template>
3.1 添加子组件
创建 component 组件,在 pages 目录下创建 components 目录,在创建 Demo.vue 组件
直接在主页面引用即可,nuxt 自动实现了引入 components 下的子组件
6. 动态路由
如果你想要创建⼀个名为 user 的⻚⾯,可以在 pages/user ⽬录下创建⼀个名为 [id]. vue 的⽂件 呈现⼀个 标记,其中 href 属性设置为⻚⾯的路由。
<template>
<h1>User {{ route.params.id }}</h1>
<h1>User-query {{ route.query.id }}</h1>
<a-button type="primary">
<NuxtLink to="/about">NuxtLink跳转About</NuxtLink>
</a-button>
<br />
<a-button type="primary" @click="goToRouter"> 路由方法跳转About </a-button>
</template>
<script setup>
// import { useRoute } from "nuxt/app";
const route = useRoute();
const router = useRouter();
const goToRouter = () => {
router.push("/about");
};
console.log("route", route);
</script>
7.Nuxt 3 路由中间件
Nuxt 3 中提供了⼀个路由中间件,它允许在路由跳转前或者路由跳转后执⾏⼀些代码,例如进⾏⻚⾯访 问权限的验证、记录⽤户的访问记录等等。 我们在 pages 目录下 middleware/ 创建⼀个 auth.js 中间件
- 具名的路由中间件
export default function ({ store, redirect }) {
// 判断⽤户是否已经登录
// 假设
console.log("路由中间件执行了");
let authUser = false;
if (!authUser) {
return navigateTo("/login");
}
}
使用中间件,在对应的页面中添加 middleware: "auth"
definePageMeta({
middleware: "auth",
});
- 全局路由中间件 middleware/_**.global.js _/
export default defineNuxtRouteMiddleware((to, from) => {
console.log(`1.全局路由中间件 to: ${to.path}, from: ${from.path}`);
});
3.匿名(或内联)路由中间件
definePageMeta({
middleware: [
"auth",
"auth-other",
defineNuxtRouteMiddleware(() => {
console.log("4.匿名(或内联)路由中间件执行了");
let authUser = false;
if (!authUser) {
return navigateTo("/home");
}
}),
],
});
8.插件 plugins-公共方法
只有在 plugins/ ⽬录的顶层的⽂件(或任何⼦⽬录中的索引⽂件)才会被注册为插件
plugins
| - myPlugin.ts
| - myOtherPlugin
| |-- supportingFile.ts
| |-- componentToRegister.vue
| |-- index.ts
//plugins/myPlugin.ts
export default defineNuxtPlugin((nuxtApp) => {
return {
//⾃动提供辅助函数,返回辅助函数
provide: {
myPlugin: (msg: string) => `Hello ${msg}!`,
},
};
});
9.Ant ⾮组件模块处理
之前我们按需引⼊蚂蚁框架组件时,官⽅提示⾮组件不能直接使⽤。我们需要⼿动引⼊
//pages/index.vue
<template>
...
<a-button type="primary" @click="info">消息弹框</a-button>
</template>
<script setup>
...
import 'ant-design-vue/es/message/style/css';
import {message} from "ant-design-vue";
const info = () => {
message.info('这是⼀个消息')
}
</script>
我们不想每次使⽤都要⼿动引⼊,我们可以将其封装成插件。在 plugins/ ⽬录下创建 antPlugin.ts ⽂件,
import 'ant-design-vue/es/message/style/css'
import 'ant-design-vue/es/notification/style/css'
import { message ,notification } from 'ant-design-vue';
export default defineNuxtPlugin(nuxtApp => {
return {
//⾃动提供辅助函数
provide: {
message: message,
notification: notification
}
}
})
在使⽤的⻚⾯上:这样⼦我们就不⽤⼿动引⼊了
//pages/about.vue
<template>
...
<a-button type="primary" @click="info">消息弹框</a-button>
</template>
<script setup>
...
const { $message } = useNuxtApp()
const info = () => {
$message.info('这是⼀个消息')
}
</script>
10. pina 安装和使用
1.安装
pnpm add pinia @pinia/nuxt
2.配置项
// nuxt.config.js
modules: ['@pinia/nuxt'],
3.创建⼀个 store ,在 pages/store 文件夹下创建 myStore.ts
import { defineStore } from 'pinia'
export const useMyStore = defineStore('myStore',{
state: () => ({
counter: 1
}),
getters: {
doubleCounter:(state) => state.counter * 2
},
actions: {
add(){
this.counter++
}
}
})
4.在页面中使用
在⻚⾯上使⽤
<template>
<!-- vue3页面 -->
<div>人生何处不相逢</div>
引用子组件
<demo />
<NuxtLink
:to="{ path: '/user/58', query: { msg: JSON.stringify(msg), id: 85 } }"
>
NuxtLink带参数跳转
</NuxtLink>
<h1>plugins-公共方法引用</h1>
<div>
{{ $myPlugin("world") }}
</div>
<h1>Ant⾮组件模块处理</h1>
<a-button type="primary" @click="info">消息弹框</a-button>
<h1>pina的使用</h1>
<div>数量:{{ counter }} getter-数量:{{ doubleCounter }}</div>
<a-button type="primary" @click="countAdd">数量+1</a-button>
</template>
<script lang="ts" setup>
// import "ant-design-vue/es/message/style/index";
// import { message } from "ant-design-vue";
import { storeToRefs } from "pinia";
import { useMyStore } from "./store/myStore";
const myStore = useMyStore();
definePageMeta({
middleware: [
"auth",
"auth-other",
defineNuxtRouteMiddleware(() => {
console.log("4.匿名(或内联)路由中间件执行了");
// let authUser = false;
// if (!authUser) {
// return navigateTo("/home");
// }
}),
],
});
// 也可以在这⾥使⽤
const { $myPlugin } = useNuxtApp();
const msg = {
id: 1,
author: "hedy",
book: "nuxt3 全栈学习",
};
// message弹框
const { $message } = useNuxtApp();
const info = () => {
// message.info("这是⼀个消息");
$message.info("这是⼀个消息");
};
// TODO pina store使用
const { counter, doubleCounter } = storeToRefs(myStore);
console.log("pina-myStore", counter, doubleCounter);
const countAdd = () => {
myStore.add();
};
</script>
<style scoped></style>
5.持久话处理
// https://nuxt.com/docs/api/configuration/nuxt-config
import Components from "unplugin-vue-components/vite";
import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
export default defineNuxtConfig({
compatibilityDate: "2025-07-15",
devtools: { enabled: true },
// modules: ['@ant-design-vue/nuxt'],
vite: {
plugins: [
// 按需引⼊组件
Components({
resolvers: [AntDesignVueResolver({ importStyle: false })],
}),
],
// ssr
ssr: {
noExternal: ["ant-design-vue"],
},
},
modules: ["@pinia/nuxt", "@pinia-plugin-persistedstate/nuxt"],
piniaPersistedstate: {
// 全局默认配置
storage: "sessionStorage", // 可选 'localStorage' | 'sessionStorage' | 'cookies'
cookieOptions: {
// 如果选择 'cookies',可以配置 Cookie 选项
maxAge: 3600, // 1 小时过期
sameSite: "lax",
secure: true, // 仅 HTTPS
},
},
});
缓存某一项的 store/myStore.js
import { defineStore } from "pinia";
export const useMyStore = defineStore("myStore", {
state: () => ({
counter: 1,
}),
getters: {
doubleCounter: (state) => state.counter * 2,
},
persist: true,
actions: {
add() {
this.counter++;
},
},
});
11.状态管理之 useState
11.1 共享状态
通过使用 auto-imported composables,我们可以定义全局类型安全状态并在整个应用中导入它们。 我们在项目根目录下面创建 composables/ 目录,用来放我们的状态。 创建 composables/states.ts :
export const useCounter = () => useState<number>('counter', () => 0)
export const useColor = () => useState<string>('color', () => 'pink')
<template>
<!-- vue3页面 -->
<div>恭喜发财-home</div>
<h1>pina的使用</h1>
<div>数量:{{ counter }} getter-数量:{{ doubleCounter }}</div>
<a-button type="primary" @click="countAdd">数量+1</a-button></br>
<h1>useState</h1>
<div>颜色{{color}}</div>
<a-button type="primary" @click="changeColor">颜色修改</a-button></br>
</template>
<script lang="ts" setup>
import { storeToRefs } from "pinia";
import { useMyStore } from "./store/myStore";
const myStore = useMyStore();
const { counter, doubleCounter } = storeToRefs(myStore);
const countAdd = () => {
myStore.add();
};
// useState使用
const color = useColor() // Same as useState('color')
//重新赋值
const changeColor = () => {
const colors = ["red", "blue", "green", "pink", "yellow", "purple", "orange", "black", "white", "gray"];
color.value = colors[Math.ceil(Math.random()*10)] || 'black';//像赋值普通ref一样给全局状态赋值
};
</script>
<style scoped></style>
12.useCookie 管理
Nuxt 3 提供了一个组合式函数 useCookie() 来让我们可以读写 Cookie。
const cookie = useCookie(name, options)
name : 对应的就是 cookie 的 key。 options : 设置多个 cookie 属性
- maxAge : 指定 Max-Age 属性的值,单位是秒。如果没有设置,则这个 cookie 将会是 Session Only,意即网页关闭后就会消失。
- expires : 指定一个 Date 物件来作为过期的时间,通常是要相容比较旧的浏览器做使用,如果 maxAge 与 expires 属性都有设定,则过期时间应该要设定为一样。
- httpOnly : 是一个布尔值,默认为 false,当设置为 true 时,表示客户端的 JavaScript 将无法使用 document.cookie 来查看这个 cookie。通常是比较敏感或机密的讯息,如 Token 或 Session Id 会设 定为 true,只让浏览器发出请求时自动夹带。
- secure : 是一个布尔值,默认为 false,当设置为 true 时浏览器得是 HTTPS 的加密传输协定的情境 下,才会自动夹带这个 cookie。
- domain : 指定 cookie 可以适用的 Domain,通常会保持预设,表是适用于自己的 Domain 之下。默 认情况下,未设置任何域,大多数客户端将认为 cookie 仅适用于当前域。
- path : 指定 cookie 适用的路径。
- sameSite : 用于设定安全策略。
- encode : 由于 cookie 的值只能使用有限的字元集,所以这个设置可以将 cookie 编码成合法的字串 值,默认的编码是使用 JSON.stringify+ encodeURIComponent()。
- decode : cookie 会经过一个解码的过程,默认的解码是使用 decodeURIComponent+ destr。
- default : 为一个函数,可以用于回传 cookie 的默认值,也可以是回传一个 Ref。
demo
<template>
<div>
<h1>Counter: {{ counter || '-' }}</h1>
<button @click="counter = null">reset</button>
<button @click="counter--">-</button>
<button @click="counter++">+</button>
</div>
</template>
<script setup>
const counter = useCookie('counter')
counter.value = counter.value || Math.round(Math.random() * 1000)
</script>
13.全局和局部样式
13.1 全局引入
静态资源一般放在 assets/ 目录下。我们在 assets/css/ 目录下创建 全局样式的文件 global.scss
//global.scss
.j-flex{
display: flex;
}
.j-sb{
justify-content: space-between;
}
.f-24 {
font-size: 24px;
}
.f-36 {
font-size: 36px;
}
在 nuxt.config.ts 中配置:
export default defineNuxtConfig({
css: [
'~/assets/css/global.css'
],
//...
})
13.2 局部样式文件
局部样式我们可以直接在页面上写
13.3 改成 scss
下载依赖
pnpm i sass-embedded -D
14.数据获取 useFetch()
useFetch() 是 Nuxt3 中的一个新的 API ,用于在组件中进行数据获取操作。它类似于 Vue 3 中 的 setup() 钩子。 Nuxt 封装提供了 4 个函数 useFetch、useLazyFetch、useAsyncData 和 useLazyAsyncData 来处理应 用程序中的数据获取。useFetch 是基于另外 3 个函数的封装。 useFetch() 钩子可以让你方便地获取远程数据,将数据存储在组件的状态中,并在组件中使用数据。
// TODO 接口请求
const imgSrc=ref('')
const param1 = ref('value1')
const res = await useFetch('https://dog.ceo/api/breeds/image/random',{
query: { param1, param2: 'value2' },
server: false //设置为false就可以在浏览器中看到
})
imgSrc.value = res.data.value?.message
console.log("接口请求处理中...", res.data.value?.message)