Nuxt.js高级路由配置
学习目标
通过本章节的学习,你将能够:
- 了解Nuxt.js路由优先级的规则
- 掌握路由中间件的高级使用方法
- 学会实现路由动画和过渡效果
- 掌握路由守卫的高级应用
- 了解路由配置的最佳实践
核心知识点
路由优先级
路由匹配规则
Nuxt.js的路由系统基于文件系统,路由优先级按照以下规则确定:
- 静态路由:具有固定路径的路由(如
/pages/about.vue) - 动态路由:包含参数的路由(如
/pages/users/_id.vue) - ** catch-all 路由**:匹配任意路径的路由(如
/pages/[...slug].vue) - 404 页面:匹配所有未找到的路由(如
/pages/404.vue)
优先级示例
/pages/
├── about.vue # 优先级 1
├── users/ # 优先级 2
│ ├── _id.vue # 匹配 /users/1, /users/2 等
│ └── index.vue # 匹配 /users
├── [slug].vue # 优先级 3
├── [...slug].vue # 优先级 4
└── 404.vue # 优先级 5路由中间件
全局中间件
全局中间件会应用于所有路由:
// middleware/auth.js
export default defineNuxtRouteMiddleware((to, from) => {
const user = useUserStore().user
if (!user && to.path !== '/login') {
return navigateTo('/login')
}
})在 nuxt.config.ts 中配置全局中间件:
export default defineNuxtConfig({
routeRules: {
'/**': {
middleware: 'auth'
}
}
})页面级中间件
页面级中间件只应用于特定页面:
<!-- pages/dashboard.vue -->
<template>
<div class="dashboard">
<!-- 内容 -->
</div>
</template>
<script setup>
definePageMeta({
middleware: 'auth'
})
</script>命名中间件
命名中间件可以根据条件应用:
// middleware/admin.js
export default defineNuxtRouteMiddleware((to, from) => {
const user = useUserStore().user
if (!user || !user.isAdmin) {
return navigateTo('/')
}
})在页面中使用:
<!-- pages/admin/index.vue -->
<template>
<div class="admin">
<!-- 内容 -->
</div>
</template>
<script setup>
definePageMeta({
middleware: 'admin'
})
</script>路由动画和过渡效果
基本过渡效果
使用 Vue 的 <transition> 组件实现路由过渡:
<!-- app.vue -->
<template>
<div>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</template>
<style>
.page-enter-active,
.page-leave-active {
transition: opacity 0.3s ease;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
}
</style>自定义过渡效果
为不同路由定义不同的过渡效果:
<!-- app.vue -->
<template>
<div>
<NuxtLayout>
<NuxtPage :transition="routeTransition" />
</NuxtLayout>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useRoute } from 'nuxt/app'
const route = useRoute()
const routeTransition = computed(() => {
// 根据路由路径返回不同的过渡效果
if (route.path.startsWith('/products')) {
return {
name: 'product-transition',
mode: 'out-in'
}
}
return {
name: 'page',
mode: 'out-in'
}
})
</script>
<style>
/* 基本过渡效果 */
.page-enter-active,
.page-leave-active {
transition: opacity 0.3s ease;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
}
/* 商品页面过渡效果 */
.product-transition-enter-active,
.product-transition-leave-active {
transition: all 0.5s ease;
}
.product-transition-enter-from {
opacity: 0;
transform: translateX(30px);
}
.product-transition-leave-to {
opacity: 0;
transform: translateX(-30px);
}
</style>路由动画
使用 CSS 动画实现更复杂的路由效果:
<!-- app.vue -->
<template>
<div>
<NuxtLayout>
<NuxtPage :transition="{
name: 'fade',
mode: 'out-in'
}" />
</NuxtLayout>
</div>
</template>
<style>
.fade-enter-active {
animation: fade-in 0.5s ease-out;
}
.fade-leave-active {
animation: fade-out 0.5s ease-in;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fade-out {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-20px);
}
}
</style>路由守卫高级应用
全局路由守卫
全局路由守卫可以在 plugins/router.js 中配置:
// plugins/router.js
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:start', () => {
// 页面加载开始
console.log('Page loading started')
})
nuxtApp.hook('page:finish', () => {
// 页面加载完成
console.log('Page loading finished')
})
nuxtApp.hook('page:error', (error) => {
// 页面加载错误
console.error('Page loading error:', error)
})
})路由守卫中间件
使用中间件实现更复杂的路由守卫逻辑:
// middleware/guest.js
export default defineNuxtRouteMiddleware((to, from) => {
const user = useUserStore().user
if (user && to.path === '/login') {
return navigateTo('/')
}
})// middleware/auth.js
export default defineNuxtRouteMiddleware((to, from) => {
const user = useUserStore().user
// 检查用户是否登录
if (!user) {
return navigateTo('/login')
}
// 检查用户权限
if (to.meta.requiresAdmin && !user.isAdmin) {
return navigateTo('/')
}
})在页面中使用:
<!-- pages/admin.vue -->
<template>
<div class="admin">
<!-- 内容 -->
</div>
</template>
<script setup>
definePageMeta({
middleware: 'auth',
meta: {
requiresAdmin: true
}
})
</script>路由配置的最佳实践
- 合理组织路由结构:根据业务逻辑组织路由文件
- 使用命名路由:为路由设置有意义的名称
- 添加路由元信息:使用
meta字段添加路由相关信息 - 实现路由过渡:为路由添加适当的过渡效果
- 使用路由中间件:集中处理路由相关的逻辑
- 优化路由性能:避免嵌套过深的路由结构
- 添加路由守卫:保护敏感路由
- 实现404页面:为未找到的路由提供友好的错误页面
实用案例分析
案例一:实现路由权限系统
功能需求
实现一个基于角色的路由权限系统,包括:
- 普通用户只能访问公开页面
- 登录用户可以访问个人中心
- 管理员可以访问所有页面
实现步骤
- 创建用户状态管理
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
user: null,
token: localStorage.getItem('token')
}),
getters: {
isAuthenticated: (state) => !!state.token,
isAdmin: (state) => state.user?.role === 'admin'
},
actions: {
login(userData) {
this.user = userData
this.token = userData.token
localStorage.setItem('token', userData.token)
},
logout() {
this.user = null
this.token = null
localStorage.removeItem('token')
},
async fetchUser() {
if (this.token) {
// 从API获取用户信息
try {
const response = await $fetch('/api/user', {
headers: {
Authorization: `Bearer ${this.token}`
}
})
this.user = response
} catch (error) {
this.logout()
}
}
}
}
})- 创建权限中间件
// middleware/auth.js
export default defineNuxtRouteMiddleware(async (to, from) => {
const userStore = useUserStore()
// 检查是否有token
if (userStore.token) {
// 获取用户信息
await userStore.fetchUser()
}
// 检查路由是否需要认证
if (to.meta.requiresAuth) {
if (!userStore.isAuthenticated) {
return navigateTo('/login')
}
// 检查是否需要管理员权限
if (to.meta.requiresAdmin && !userStore.isAdmin) {
return navigateTo('/')
}
}
// 检查是否是登录页面,已登录用户不需要访问
if (to.path === '/login' && userStore.isAuthenticated) {
return navigateTo('/')
}
})- 配置路由权限
<!-- pages/index.vue -->
<template>
<div class="home">
<!-- 首页内容 -->
</div>
</template>
<script setup>
definePageMeta({
middleware: 'auth'
})
</script><!-- pages/dashboard.vue -->
<template>
<div class="dashboard">
<!-- 个人中心内容 -->
</div>
</template>
<script setup>
definePageMeta({
middleware: 'auth',
meta: {
requiresAuth: true
}
})
</script><!-- pages/admin/index.vue -->
<template>
<div class="admin">
<!-- 管理员内容 -->
</div>
</template>
<script setup>
definePageMeta({
middleware: 'auth',
meta: {
requiresAuth: true,
requiresAdmin: true
}
})
</script>案例二:实现路由动画系统
功能需求
实现一个具有丰富动画效果的路由系统,包括:
- 首页进入/离开动画
- 商品页面进入/离开动画
- 详情页面进入/离开动画
实现步骤
- 配置全局过渡效果
<!-- app.vue -->
<template>
<div>
<NuxtLayout>
<NuxtPage :transition="getPageTransition" />
</NuxtLayout>
</div>
</template>
<script setup>
import { useRoute } from 'nuxt/app'
const route = useRoute()
const getPageTransition = () => {
// 根据路由路径返回不同的过渡效果
if (route.path === '/') {
return {
name: 'home-transition'
}
} else if (route.path.startsWith('/products')) {
return {
name: 'product-transition'
}
} else if (route.path.match(/\/products\/\d+/)) {
return {
name: 'detail-transition'
}
}
// 默认过渡效果
return {
name: 'default-transition'
}
}
</script>
<style>
/* 默认过渡效果 */
.default-transition-enter-active,
.default-transition-leave-active {
transition: opacity 0.3s ease;
}
.default-transition-enter-from,
.default-transition-leave-to {
opacity: 0;
}
/* 首页过渡效果 */
.home-transition-enter-active {
animation: home-in 1s ease-out;
}
.home-transition-leave-active {
animation: home-out 1s ease-in;
}
@keyframes home-in {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes home-out {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(1.2);
}
}
/* 商品列表过渡效果 */
.product-transition-enter-active,
.product-transition-leave-active {
transition: all 0.5s ease;
}
.product-transition-enter-from {
opacity: 0;
transform: translateX(30px);
}
.product-transition-leave-to {
opacity: 0;
transform: translateX(-30px);
}
/* 商品详情过渡效果 */
.detail-transition-enter-active,
.detail-transition-leave-active {
transition: all 0.6s ease;
}
.detail-transition-enter-from {
opacity: 0;
transform: translateY(20px);
}
.detail-transition-leave-to {
opacity: 0;
transform: translateY(-20px);
}
</style>- 创建页面组件
<!-- pages/index.vue -->
<template>
<div class="home">
<h1>首页</h1>
<NuxtLink to="/products">查看商品</NuxtLink>
</div>
</template><!-- pages/products/index.vue -->
<template>
<div class="products">
<h1>商品列表</h1>
<div v-for="product in products" :key="product.id" class="product-card">
<h2>{{ product.name }}</h2>
<NuxtLink :to="`/products/${product.id}`">查看详情</NuxtLink>
</div>
</div>
</template>
<script setup>
const { data: products } = useAsyncData('products', () => {
return $fetch('/api/products')
})
</script><!-- pages/products/_id.vue -->
<template>
<div class="product-detail">
<h1>{{ product.name }}</h1>
<p>{{ product.description }}</p>
<NuxtLink to="/products">返回列表</NuxtLink>
</div>
</template>
<script setup>
const route = useRoute()
const { data: product } = useAsyncData('product', () => {
return $fetch(`/api/products/${route.params.id}`)
})
</script>总结
本章节介绍了Nuxt.js的高级路由配置,包括:
- 路由优先级:了解了路由匹配的规则和优先级顺序
- 路由中间件:掌握了全局中间件和页面级中间件的使用
- 路由动画和过渡效果:学会了如何实现基本和自定义的路由过渡效果
- 路由守卫的高级应用:掌握了基于角色的权限系统实现
- 路由配置的最佳实践:了解了路由组织和管理的最佳实践
通过本章节的学习,你应该能够实现复杂的路由系统,包括权限控制、动画效果和高级导航逻辑。这些技术将帮助你创建更加用户友好和功能完整的Nuxt.js应用。