前端笔记-Vue router
学习目标
Vue Router路由管理 | 1、路由配置 |
2、嵌套路由 | |
3、路由守卫与权限控制 |
一、路由配置(给网站做地图)
npm i vue-router
作用:告诉浏览器什么地址该显示什么页面
核心代码:
// 创建路由并暴露出去// 第一步:引入createRouter方法
import { createRouter, createWebHashHistory} from 'vue-router'//引入一个个路由组件
import Home from '@/components/Home.vue'
import About from '@/components/About.vue'
import News from '@/components/News.vue'// 第二步:创建路由实例
const router = createRouter({history:createWebHashHistory(),routes:[{path:'/home',component:Home},{path:'/about',component:About},{path:'/news',component:News }]
})// 暴露出去router
export default router
使用场景:
- 点击导航菜单切换页面
- 直接输入网址访问特定页面
二、嵌套路由(套娃页面)
作用:在页面内部再嵌套子页面(如后台s
示例:
const routes = [{path: '/user',component: UserLayout, // 父组件(带公共布局)children: [ // 子路由列表{path: '', // /usercomponent: UserHome},{path: 'profile', // /user/profilecomponent: UserProfile}]}
]
三、路由守卫与权限控制(保安检查)
作用:控制谁能进入页面(如登录验证)
1. 全局守卫(大门保安)
router.beforeEach((to, from, next) => {if (to.path === '/admin' && !isLogin()) {next('/login') // 跳转到登录页} else {next() // 放行}
})
2. 路由独享守卫(房间保安)
{path: '/dashboard',component: Dashboard,beforeEnter: (to, from, next) => {// 专门检查这个路由的权限}
}
3. 组件内守卫(内部监控)
export default {beforeRouteEnter(to, from, next) {// 进入组件前调用},beforeRouteLeave(to, from, next) {// 离开组件时调用if (hasUnsavedChanges) {return confirm('确定离开吗?未保存的数据会丢失!')}}
}
四、路由系统三大核心要素
-
导航区(控制中心)
- 使用
<router-link>
组件代替传统<a>
标签 - 示例:
<div class="nav"><router-link to="/">首页</router-link><router-link to="/about">关于</router-link> </div>
- 使用
-
展示区(动态画布)
- 用
<router-view>
作为内容渲染出口 - 示例:
<div class="content"><router-view></router-view> <!-- 这里显示路由对应的组件 --> </div>
- 用
-
路由器(指挥中心)
- 创建步骤:
// 1. 安装路由 npm install vue-router@4// 2. 创建路由实例(router/index.js) import { createRouter, createWebHistory } from 'vue-router'const router = createRouter({history: createWebHistory(),routes: [] // 路由规则稍后添加 })
- 创建步骤:
五、路由规则配置(交通法规)
-
基本路由规则
const routes = [{path: '/', // 访问路径name: 'home', // 路由名称(可选)component: Home // 对应的组件},{path: '/about',component: () => import('@/views/About.vue') // 懒加载} ]
-
动态路由示例
{path: '/user/:id', // 动态参数component: UserDetail }
Vue Router 工作模式
两种模式对比
特性 | History 模式 | Hash 模式 |
---|---|---|
URL美观度 | 无#,美观 (e.g. /user/profile) | 带# (e.g. /#/user/profile) |
服务器要求 | 需要特殊配置 | 无需特殊配置 |
SEO优化 | 友好 | 较差 |
兼容性 | 现代浏览器 | 所有浏览器 |
刷新行为 | 需服务器支持,否则404 | 正常加载 |
1. History 模式
原理:基于HTML5 History API (pushState
, replaceState
)
const router = createRouter({history: createWebHistory(), // 使用history模式routes: [...]
})
特点:
- ✅ URL更简洁:
https://example.com/user/profile
- ✅ 对SEO更友好
- ❌ 需要服务器配置:
# Nginx示例配置 location / {try_files $uri $uri/ /index.html; }
2. Hash 模式
原理:基于URL hash (#
)
const router = createRouter({history: createWebHashHistory(), // 使用hash模式routes: [...]
})
特点:
- ✅ 开箱即用,无需服务器配置
- ✅ 兼容IE9等老旧浏览器
- ❌ URL包含#:
https://example.com/#/user/profile
- ❌ 服务端渲染(SSR)不支持
选择建议
- 开发环境:两种模式均可,优先测试history模式
- 生产环境:
- 有服务器控制权 → 选择history模式
- 静态网站托管(如GitHub Pages) → 选择hash模式
- SEO要求高:必须使用history模式+SSR
Vue Router 传参
1. Query参数(适合筛选/非必要参数)
News.vue
<template><div class="news"><!-- 导航区 --><ul><li v-for="(news,index) in newsList" :key="index"><RouterLink:to="{path:'news/detail',query:{id:index,title:news.title,date:news.date,category:news.category,brief:news.brief,author:news.author,content:news.content}}">{{ news.title }}</RouterLink></li></ul><!-- 新闻详情区 --><div class="news-container"><RouterView></RouterView></div></div>
</template>
特点:
- URL格式:
/news/detail?id=0&from=news-list
- 通过
route.query.id
获取 - 适用场景:分页、筛选等可选参数
- 图片风格适配:保持绿色按钮+深蓝激活态
对应Detail.vue
<template><div class="news-detail-container"><h3>{{query.title}}</h3><div class="news-detail-meta"><span>发布日期:{{ query.date }}</span><span>作者:{{ query.author }}</span><span>分类:{{ query.category }}</span></div><div class="news-detail-content"><p>{{ query.content }}</p></div></div>
</template><script lang="ts" setup>
import { toRefs} from 'vue'
import { useRoute } from 'vue-router'let route = useRoute()
let {query} = toRefs(route)</script>
2. Params参数(适合必要参数)
上述同样的代码,我们换成params传参,需要注意的是,在router/index.ts文件路由路径中需要提前占位(即要传什么参数,先提前写好)
路由配置:router/index.ts
const routes: RouteRecordRaw[] = [{path: '/news',component: () => import('@/views/News.vue'),children: [{name: 'xiang', path: 'detail/:id/:title/:date/:author/:category/:content',component: () => import('@/views/Detail.vue')}]}
]
News.vue
<template><div class="news"><!-- 导航区 --><ul><li v-for="(news,index) in newsList" :key="index"><RouterLink:to="{name: 'xiang', // 使用图片中的路由名称params: { id: index,title: news.title,date: news.date,author: news.author,category: news.category,content: news.content.join('|') // 将数组转换为字符串}}">{{ news.title }}</RouterLink></li></ul><!-- 新闻详情区 --><div class="news-container"><RouterView></RouterView></div></div>
</template>
Detail.vue
<template><div class="news-detail-container"><h3>{{ title }}</h3><div class="news-detail-meta"><span>发布日期: {{ date }}</span><span>作者: {{ author }}</span><span>分类: {{ category }}</span></div><div class="news-detail-content"><p v-for="(para, index) in parsedContent" :key="index">{{ para }}</p></div></div>
</template><script lang="ts" setup>
import { computed } from 'vue'const props = defineProps({id: String,title: String,date: String,author: String,category: String,content: String
})// 将字符串转换回数组
const parsedContent = computed(() => {return props.content?.split('|') || []
})
</script>
特点:
- URL格式:
/news/detail/1
- 通过
route.params.id
获取 - 适用场景:新闻ID等必要参数
- 图片风格适配:卡片内链接触发绿色悬停效果
3. 路由的Props配置
路由配置:router/index.ts
const routes: RouteRecordRaw[] = [{path: '/news',component: () => import('@/views/News.vue'),children: [{name: 'xiang', path:'detail',component:Detail,props: (route) => route.query}]}
]
props还有其他几种写法
// props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件// props:{a:1,b:2,c:3}, // props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件// props:true// props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件props(route){return route.query}
News.vue
<template><div class="news"><!-- 导航区 --><ul><li v-for="(news,index) in newsList" :key="index"><RouterLink:to="{path:'/news/detail',query: { // 直接传所有数据id: index,...news }}">{{ news.title }}</RouterLink></li></ul><!-- 新闻详情区 --><div class="news-container"><RouterView></RouterView></div></div>
</template>
Detail.vue
<template><div class="news-detail-container"><h3>{{title}}</h3><div class="news-detail-meta"><span>发布日期:{{ date }}</span><span>作者:{{author }}</span></div><div class="news-detail-content"><p>{{ content }}</p></div></div>
</template><script lang="ts" setup>defineProps(['id', 'title', 'date', 'author', 'content'])</script>
优势:
- 解耦组件和路由
- 类型安全(TypeScript友好)
传参方式对比表格
方式 | URL示例 | 获取方法 | 适用场景 | 图片UI对应位置 |
---|---|---|---|---|
Query | /news?id=1&from=list | route.query.id | 筛选、排序等可选参数 | 左侧导航栏筛选按钮 |
Params | /news/1 | route.params.id | 详情页ID等必要参数 | 右侧新闻卡片跳转链接 |
Props | /news/1 | 组件props | 需要类型检查的场景 | 内容区详情组件 |
补充:编程式路由导航
基础编程式导航
1. 路由跳转(替换当前历史记录)
import { useRouter } from 'vue-router'const router = useRouter()// 跳转到指定路径
router.push('/news') // 带参数跳转(匹配您的图片配置)
router.push({path: '/news/detail',query: { id: 1,title: 'Vue 3.4 发布',content: JSON.stringify(['内容1', '内容2'])}
})// 命名路由跳转(推荐)
router.push({name: 'xiang',params: { id: 1 },query: { title: 'Vue 3.4 发布' }
})
2. 路由替换(无历史记录)
router.replace('/about') // 替换当前条目
3. 历史记录控制
router.go(1) // 前进
router.go(-1) // 后退
router.back() // 等价于 go(-1)
动态路由实践
1. 带参数的新闻跳转
// 在新闻列表中的点击事件
const gotoDetail = (news, index) => {router.push({path: '/news/detail',query: {id: index,...news,content: JSON.stringify(news.content) // 数组需序列化}})
}
2. 接收参数(两种方式)
// 方式1:通过 props 接收(推荐)
defineProps({id: [String, Number],title: String,content: String // 需自行反序列化
})// 方式2:通过路由实例
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.id) // 获取query参数
三、高级应用技巧
1. 路由守卫控制
router.beforeEach((to, from) => {if (to.path === '/news/detail' && !to.query.id) {return '/news' // 拦截无效访问}
})
2. 动态路由追加(根据权限)
router.addRoute({path: '/admin',component: () => import('@/views/Admin.vue')
})
3. 路由元信息(扩展功能)
// 路由配置中
meta: { requiresAuth: true }// 组件中获取
const route = useRoute()
console.log(route.meta.requiresAuth)
项目实践
1. 路由参数处理优化
// 在 detail.vue 中安全解析参数
const parsedContent = computed(() => {try {return JSON.parse(props.content || '[]')} catch {return ['无效内容格式']}
})
2. 类型安全(TypeScript)
interface NewsParams {id: string | numbertitle: stringcontent: string
}const props = defineProps<NewsParams>()
3. 导航失败处理
router.push('/news').catch(err => {console.error('导航失败:', err)
})
常见问题解决方案
1. 路由重复跳转报错
// 在 router/index.js 中
const router = createRouter({history: createWebHashHistory(),routes,strict: true // 严格模式
})
2. 刷新后参数丢失
// 使用 localStorage 做持久化
router.push({path: '/news/detail',query: {id: localStorage.getItem('lastNewsId') }
})
3. 滚动行为控制
const router = createRouter({scrollBehavior(to) {if (to.hash) {return { el: to.hash }}return { top: 0 }}
})
补充:路由重定向
基础重定向配置
const routes = [{path: '/',redirect: '/news' // 首页重定向到新闻页},{path: '/news',component: News,children: [{path: 'detail',redirect: { name: 'xiang', // 命名路由重定向query: { id: 0 } // 默认显示第一条新闻}}]}
]
动态重定向(适合新闻详情页)
1. 根据条件重定向
{path: '/old-news/:id',redirect: to => ({path: '/news/detail',query: { id: to.params.id } // 保持参数不变})
}
2. 404重定向
{path: '/:pathMatch(.*)*',redirect: '/news' // 所有未知路径跳转新闻首页
}
路由守卫重定向(权限控制)
router.beforeEach((to, from) => {// 模仿图片中的新闻详情页鉴权if (to.path === '/news/detail' && !to.query.id) {return { path: '/news',query: { redirect: to.fullPath } // 保存目标路径}}
})
视觉友好型重定向
1. 带过渡效果的重定向
router.afterEach((to, from) => {// 图片中的浅绿色进度条效果document.querySelector('.progress-bar').style.width = '100%'setTimeout(() => {document.querySelector('.progress-bar').style.width = '0'}, 500)
})
2. 重定向状态提示(类似图片中的发布日期样式)
<template><div v-if="isRedirecting" class="redirect-notice"><p>正在跳转到新闻页...</p><div class="loading-bar" style="background-color: #b6edd6;"></div></div>
</template>
组件文件结构示例
src/
├── router/
│ └── index.js # 路由配置文件
├── views/ # 路由级组件
│ ├── Home.vue
│ ├── About.vue
│ └── News/
│ ├
│ └── Detail.vue
└── App.vue # 根组件(包含router-view)
完整工作流程实战
-
App.vue(主框架)
<template><div class="app"><h2 class="title">Vue路由测试</h2><!-- 导航区 --><div class="navigate"><RouterLink to="/home" active-class="active">首页</RouterLink><RouterLink to="/news" active-class="active">新闻</RouterLink><RouterLink to="/about" active-class="active">关于</RouterLink></div><!-- 展示区 --><div class="main-content"><RouterView></RouterView></div></div> </template><script lang="ts" setup name="App">import {RouterView,RouterLink} from 'vue-router' </script>
-
路由配置(router/index.js)
// 创建路由并暴露出去// 第一步:引入createRouter方法 import { createRouter, createWebHashHistory} from 'vue-router'//引入一个个路由组件 import Home from '@/views/Home.vue' import About from '@/views/About.vue' import News from '@/views/News.vue'// 第二步:创建路由实例 const router = createRouter({history:createWebHashHistory(),routes:[{path:'/home',component:Home},{path:'/about',component:About},{path:'/news',component:News }] })// 暴露出去router export default router
-
启动路由(main.js)
import { createApp } from 'vue' import App from './App.vue' import router from './router'const app = createApp(App) app.use(router) app.mount('#app')
完整效果展示:
源代码路径: