Vue - 路由
Vue 3 中的路由系统主要依赖官方的 vue-router
库(当前最新稳定版是 4.x,专为 Vue 3 设计),用于实现单页应用(SPA)的页面跳转和状态管理。以下是 Vue 3 路由的详细解析:
一、核心概念
- 路由(Route):对应单页应用中的一个 “页面”,由路径(
path
)和组件(component
)组成。 - 路由器(Router):管理所有路由的实例,通过
createRouter
创建。 - 路由出口(Router View):组件
<RouterView />
,用于渲染当前路由对应的组件。 - 路由链接(Router Link):组件
<RouterLink />
,用于生成跳转链接(替代<a>
标签,避免页面刷新)。
二、基础用法
安装
npm install vue-router@4 # Vue 3 需使用 4.x 版本
快速入门
1.创建文件router文件夹下面的index.ts
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '@/components/Home.vue'
import NewsView from '@/components/News.vue'
import AboutView from '@/components/About.vue'
const router = createRouter({
history: createWebHistory(), //路由器工作模式
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/news',
name: 'news',
component: NewsView,
},
{
path: '/about',
name: 'about',
component: AboutView,
}
],
})
// 导出路由
export default router
2.创建 Home.vue、About.vue、News.vue,地址 src\components\Home.vue
<template>
<div>
<h1>Home</h1>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
setup() {
return {
msg: 'Hello Vue 3!',
}
},
})
</script>
3.src\main.ts 配置使用路由
import { createApp } from 'vue'
// import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
// app.use(createPinia())
app.use(router)
app.mount('#app')
4.app.vue 配置RouterView
RouterView:当前路由对应的组件会渲染在这里
src\App.vue 展示区显示
<template>
<div class="app">
<h2 class="title">Vue路由测试</h2>
<!-- 导航区 -->
<div class="navigate">
<a href="#">首页</a>
<a href="#">新闻</a>
<a href="#">关于</a>
</div>
<!-- 展示区 -->
<div class="main-content">
<RouterView />
</div>
</div>
</template>
<script lang="ts" setup name="App">
import { RouterView } from 'vue-router';
</script>
5.最后我们把跳转再加上RouterLink跳转,active-class 添加点击效果
<template>
<div class="app">
<h2 class="title">Vue路由测试</h2>
<!-- 导航区 -->
<div class="navigate">
<RouterLink to="/" active-class="router-active">首页</RouterLink>
<RouterLink to="/news" active-class="router-active">新闻</RouterLink>
<RouterLink to="/about" active-class="router-active">关于</RouterLink>
</div>
<!-- 展示区 -->
<div class="main-content">
<RouterView />
</div>
</div>
</template>
<script lang="ts" setup name="App">
import { RouterView, RouterLink } from 'vue-router';
</script>
点击添加效果:
.router-active {
color: red !important;
background-color: aquamarine !important;
font-weight: bold;
}
三、两点注意
1.路由组件通常存放在pages
或views
文件来一般组件通常存放在components
文件夹。
2.通过点击导航,视觉效果上“消失”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
四、路由工作模式
-
history 模式
优点: URL 更加美观,不带有
#
,更接近传统的网站URL
。
缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有404
错误。const router = createRouter ({ history:createWebHistory (), //history 模式 /******/ })
history: createWebHistory(import.meta.env.BASE_URL),
import.meta.env.BASE_URL
import.meta
是 ECMAScript 标准中引入的一个元数据对象,用于获取与模块相关的信息。在 Vite 等构建工具中,import.meta.env
用于访问环境变量。BASE_URL
是 Vite 预设的一个环境变量,它表示项目的基础公共路径。作用
- 处理项目部署路径:当项目部署在服务器的非根路径下时,比如部署在
https://example.com/myapp/
,而不是https://example.com/
。BASE_URL
就会被设置为/myapp/
,将其传递给createWebHistory
,Vue Router 就能够正确处理所有路由路径,使得在访问不同路由时,路径都能基于这个基础路径来生成。例如,访问/about
时,实际请求的 URL 就是https://example.com/myapp/about
。 - 确保静态资源路径正确:除了路由相关,
BASE_URL
还能帮助项目中正确引用静态资源(如图片、CSS 文件、JavaScript 文件等)。如果没有正确设置,在非根路径部署的情况下,可能会导致静态资源无法正确加载。
- 处理项目部署路径:当项目部署在服务器的非根路径下时,比如部署在
-
hash 模式
优点:兼容性更好,因为不需要服务器端处理路径。
缺点: URL 带有#
不太美观,且在SEO
优化方面相对较差。const router = createRouter ({ history:createWebHashHistory (), //hash 模式 /******/ })
五、RouterLink 路由跳转方式 to
1. 路由跳转方式
-
声明式跳转:使用
<RouterLink to="路径">
(推荐,自动处理历史记录)。<!-- 基础用法 --> <RouterLink to="/news">新闻</RouterLink> <!-- 带参数的对象形式 --> <RouterLink :to="{ path: '/news', query: { id: 1 } }">新闻详情</RouterLink> <!-- 带参数的对象形式:name --> <RouterLink :to="{ name: 'aboutPage' }" active-class="router-active">关于</RouterLink>
-
编程式跳转:通过
useRouter
钩子获取路由实例,调用push
/replace
方法。<script setup> import { useRouter } from 'vue-router' const router = useRouter() // 跳转 const goToNews = () => { router.push('/news') // 字符串路径 // 或对象形式 router.push({ path: '/news', query: { id: 1 } }) } // 替换当前历史记录(无法回退) const replaceToAbout = () => { router.replace('/about') } </script>
2. 路由参数
-
Query 参数:类似 URL 中的查询字符串(
?id=1&name=test
),通过useRoute
钩子的query
属性获取。<!-- 跳转时携带参数 --> <RouterLink :to="{ path: '/news', query: { id: 1, name: '新闻1' } }">新闻1</RouterLink> <!-- 目标组件中获取参数 --> <script setup> import { useRoute } from 'vue-router' const route = useRoute() console.log(route.query.id) // 1 console.log(route.query.name) // '新闻1' </script>
-
Params 参数:路径中的动态片段(如
/news/:id
),需在路由规则中定义。// 路由规则中定义 params { path: '/news/:id', // 动态参数 id name: 'NewsDetail', component: NewsDetail }
<!-- 跳转时携带 params --> <RouterLink :to="{ name: 'NewsDetail', params: { id: 1 } }">新闻1</RouterLink> <!-- 注意:params 必须配合 name 使用,不能用 path --> <!-- 目标组件中获取 params --> <script setup> import { useRoute } from 'vue-router' const route = useRoute() console.log(route.params.id) // 1 </script>
六、嵌套路由
先把news.vue
挪动一下位置
子路由添加 children
{
path: '/news',
name: 'newsPage',
component: NewsView,
children: [
{
path: 'detail',
name: 'newsDetail',
component: NewsDetail,
},
],
},
在需要展示子路由的地方也使用 RouterView
这里用 query 方式传参
news.vue
<template>
<div class="news">
<div class="news-list">
<ul v-for="theNew in newsList" :key="theNew.id">
<li>
<RouterLink :to="{ path: '/news/detail', query: { id: theNew.id } }">{{ theNew.title }}</RouterLink>
</li>
</ul>
</div>
<!-- 展示区 -->
<div class="news-content">
<RouterView />
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
const newsList = reactive([
{ id: 'asfdtrfay01', title: '经典名著新译本推出', content: '更贴合现代读者阅读习惯' },
{ id: 'asfdtrfay02', title: '传统戏剧展演活动开启', content: '多剧种轮番登台献艺' },
{ id: 'asfdtrfay03', title: '博物馆推出新展览', content: '展示珍贵历史文物' },
{ id: 'asfdtrfay04', title: '文化创意市集热闹举行', content: '各类文创产品琳琅满目' }
]);
</script>
使用 useRoute,接收参数传值 route
detail.vue
<template>
<ul class="news-list">
<li>{{ route.query.id }}</li>
<li>标题:xxx</li>
<li>内容:xxx</li>
</ul>
</template>
<script setup lang="ts" name="detail">
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.id)
</script>
如果用 params 方式
src/router/index.ts
{
path: '/news',
name: 'newsPage',
component: NewsView,
children: [
{
path: 'detail/:id/:title/:content',
name: 'newsDetail',
component: NewsDetail,
},
],
},
src\views\news\News.vue
<RouterLink
:to="{ name: 'newsDetail', params: { id: theNew.id, title: theNew.title, content: theNew.content } }">
{{ theNew.title }}</RouterLink>
src\views\news\Detail.vue
<template>
<ul class="news-list">
<li>{{ route.params.id }}</li>
<li>{{ route.params.title }}</li>
<li>{{ route.params.content }}</li>
</ul>
</template>
<script setup lang="ts" name="detail">
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
七、props 配置
在 Vue 中,props
是组件之间传递数据的主要方式,用于从父组件向子组件传递数据。它是实现组件复用和通信的核心机制之一,以下是关于 Vue 中 props
的详细解析:
1. 子组件声明接收 props
子组件需要显式声明它期望接收的 props
,可以指定名称、类型、默认值等规则:
-
defineProps
:Vue 3<script setup>
中专门用于声明props
的宏,无需导入即可使用。 -
TypeScript 类型声明:也可以用 TypeScript 类型直接声明:
const props = defineProps<{
message: string
count?: number // 可选参数
}>()
2. 父组件传递 props
父组件在使用子组件时,通过属性的形式传递数据:
<!-- 父组件 Parent.vue -->
<template>
<div>
<!-- 传递静态值 -->
<Child message="Hello" count="10" /> <!-- 注意:这里的 "10" 是字符串,会被自动转换为数字(根据子组件类型) -->
<!-- 传递动态值(使用 v-bind 或 : 简写) -->
<Child
:message="dynamicMsg"
:count="dynamicCount"
/>
</div>
</template>
<script setup lang="ts">
import Child from './Child.vue'
import { ref } from 'vue'
const dynamicMsg = ref('动态消息')
const dynamicCount = ref(20)
</script>
例子:
{
path: '/news',
name: 'newsPage',
component: NewsView,
children: [
{
path: 'detail/:id/:title/:content',
name: 'newsDetail',
component: NewsDetail,
//第1种:将params的参数作为props传递给子组件(只能传递params参数,方便)
//props: true,
//第2种:函数式(可以传递params参数,也可以传递query参数)
props: (route) => ({
id: route.params.id,
title: route.params.title,
content: route.params.content,
RouteMessage: '动态传的属性',
}),
//第3种:对象式(可以传递params参数,也可以传递query参数)
//props: { RouteMessage: '动态传的属性' },
},
],
},
src\views\news\News.vue 父组件
<!-- 展示区 -->
<div class="news-content">
<RouterView StaticMessage="静态传的属性" />
</div>
src\views\news\Detail.vue 子组件
<template>
<ul class="news-list">
<li>{{ id }}</li>
<li>{{ title }}</li>
<li>{{ content }}</li>
<li>{{ StaticMessage }}</li>
<li>{{ RouteMessage }}</li>
</ul>
</template>
<script setup lang="ts" name="detail">
//第一种写法
// defineProps<{
// id: string,
// title: string,
// content: string
// }>();
//第二种写法
defineProps(['id', 'title', 'content', 'StaticMessage', 'RouteMessage'])
</script>
八、编程式导航
在 Vue 中,编程式导航指的是通过JavaScript 代码实现路由跳转,而不是通过 <RouterLink>
组件(声明式导航)。这在需要根据逻辑条件动态跳转的场景中非常有用(如登录成功后跳转首页)。
Vue Router 提供了一系列 API 实现编程式导航,核心方法基于路由实例(通过 useRouter
获取)。
一、核心方法
1. router.push()
最常用的跳转方法,新增一条历史记录(类似浏览器的 history.pushState
),用户点击后退按钮可以回到上一页。
基本用法:
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
// 1. 字符串路径
const goToHome = () => {
router.push('/home')
}
// 2. 对象形式(推荐,支持更多参数)
const goToNews = () => {
router.push({
path: '/news', // 路径
query: { id: 1, name: '新闻1' } // 查询参数(?id=1&name=新闻1)
})
}
// 3. 命名路由(配合路由配置中的 name 属性)
const goToAbout = () => {
router.push({
name: 'About', // 路由名称(在路由规则中定义)
params: { userId: 123 } // 动态参数(需在路由规则中声明 /about/:userId)
})
}
</script>
2. router.replace()
替换当前历史记录(类似 history.replaceState
),不会新增记录,用户点击后退会跳过当前页。
用法:
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
// 登录成功后跳转首页,不保留登录页历史
const loginSuccess = () => {
router.replace('/home')
// 也支持对象形式
router.replace({
path: '/home',
query: { from: 'login' }
})
}
</script>
3. router.go(n)
基于当前历史记录进行前进 / 后退(类似浏览器的 history.go
),n
为整数:
n = 1
:前进一页n = -1
:后退一页n = 2
:前进两页
用法:
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const goBack = () => {
router.go(-1) // 后退一页
}
const goForward = () => {
router.go(1) // 前进一页
}
</script>
便捷别名:
router.back()
:等价于router.go(-1)
(后退)router.forward()
:等价于router.go(1)
(前进)
二、参数传递
编程式导航支持两种参数传递方式:query
和 params
。
1. Query 参数(查询字符串)
- 显示在 URL 中(如
?id=1&name=test
) - 刷新页面不会丢失
- 无需在路由规则中预先定义
// 跳转时携带
router.push({
path: '/news',
query: { id: 1, name: '新闻1' }
})
// 目标组件中获取
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.id) // 1
console.log(route.query.name) // '新闻1'
2. Params 参数(动态路径参数)
- 嵌入在 URL 路径中(如
/news/1
) - 刷新页面不会丢失
- 必须在路由规则中预先定义(如
path: '/news/:id'
)
// 路由规则中定义
const routes = [
{
path: '/news/:id', // 声明动态参数 id
name: 'NewsDetail',
component: NewsDetail
}
]
// 跳转时携带(必须用 name 匹配路由,不能用 path)
router.push({
name: 'NewsDetail',
params: { id: 1 }
})
// 目标组件中获取
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // 1
三、常见场景
-
登录成功后跳转:
const handleLogin = async () => { const success = await api.login(credentials) if (success) { router.push('/dashboard') // 登录成功跳转到 dashboard } }
-
条件判断跳转:
const goToTarget = () => { if (hasPermission) { router.push('/target') } else { router.push('/forbidden') } }
-
关闭弹窗后返回上一页:
const closeModal = () => { router.back() // 关闭弹窗后后退 }
四、注意事项
-
params
与path
冲突:使用params
时,必须通过name
指定路由,不能用path
,否则params
会被忽略。// 错误:用 path 时 params 无效 router.push({ path: '/news', params: { id: 1 } }) // 正确:用 name 匹配 router.push({ name: 'NewsDetail', params: { id: 1 } })
-
路由跳转后的回调:
push
和replace
支持回调函数(适用于 Vue Router 3.x,4.x 推荐用await
):// 4.x 中推荐用 await const navigate = async () => { await router.push('/home') console.log('跳转完成') }
-
避免重复跳转同一路由报错:Vue Router 4.x 中,重复跳转同一路由会报错,可通过捕获错误解决:
const goToSameRoute = () => { try { router.push('/current-route') } catch (err) { // 忽略重复跳转错误 } }
九、路由 replace 属性
在 Vue Router 中,replace
是路由跳转的一种方式,与常见的 push
方法不同,它的核心特点是替换当前历史记录,而不是新增一条记录。
一、基本用法
1. 声明式导航(模板中)
使用 <RouterLink>
组件时,添加 replace
属性实现替换式跳转:
<!-- 普通跳转(push,新增历史记录) -->
<RouterLink to="/news">新闻</RouterLink>
<!-- 替换式跳转(replace,替换当前历史记录) -->
<RouterLink to="/news" replace>新闻</RouterLink>
2. 编程式导航(脚本中)
通过 useRouter
获取路由实例,调用 replace
方法:
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
// 编程式替换跳转
const goToNews = () => {
// 字符串路径
router.replace('/news')
// 或对象形式(支持携带参数)
router.replace({
path: '/news',
query: { id: 1 } // 携带查询参数
})
}
</script>
二、与 push
的核心区别
跳转方式 | 历史记录行为 | 后退按钮效果 | 适用场景 |
---|---|---|---|
push |
新增一条历史记录 | 点击后退会回到上一个页面 | 普通跳转(如列表 → 详情) |
replace |
替换当前历史记录 | 点击后退会跳过当前页,回到上上个页面 | 不需要保留当前页历史的场景 |
三、典型使用场景
-
登录页跳转
用户登录后跳转到首页,通常不需要保留登录页的历史记录(避免后退时回到登录页):// 登录成功后 router.replace('/home')
-
页面重定向
例如从/
重定向到/home
时,使用replace
避免用户后退到空路径:// 路由配置中 const routes = [ { path: '/', redirect: { path: '/home', replace: true } // 重定向时替换历史 } ]
-
临时页面跳转
如弹窗内的跳转、临时预览页等,不需要用户后退时再次访问的场景。
四、注意事项
replace
不会改变历史记录的数量,只是替换了当前位置的记录。- 携带参数的方式与
push
完全一致(支持query
、params
等)。 - 在 Vue Router 4.x(Vue 3 版本)中,
replace
的用法与 3.x(Vue 2 版本)完全一致,没有语法差异。
十、页面重定向
页面重定向
例如从 /
重定向到 /home
时,使用 replace
避免用户后退到空路径:
const routes = [
{
path: '/',
redirect: '/home'
}
]
// 路由配置中
const routes = [
{
path: '/',
redirect: { path: '/home', replace: true } // 重定向时替换历史
}
]
十一、路由懒加载 component: () => import('../About.vue')
路由懒加载(Route Lazy Loading)是 Vue、React 等单页应用(SPA)中优化性能的重要技术,核心思想是在需要的时候才加载路由对应的组件代码,而不是在应用初始加载时就加载所有组件。
为什么需要路由懒加载?
单页应用的特点是所有代码最终会打包成一个或少数几个 JS 文件。如果应用规模较大(有很多页面组件),初始打包的文件会非常大,导致:
- 首屏加载时间过长(用户需要等待所有代码下载完成才能看到页面)
- 浪费带宽(用户可能永远不会访问某些页面,却要下载其代码)
路由懒加载的原理
通过 ES6 的动态 import () 语法,将不同路由对应的组件分割成多个独立的代码块(chunk)。只有当用户访问该路由时,浏览器才会异步下载对应的代码块并执行。
例如,不使用懒加载时,所有组件会被打包到同一个 app.js
中:
// 不使用懒加载:初始加载时就包含 About 组件代码
import About from '../views/About.vue'
const routes = [
{ path: '/about', component: About }
]
使用懒加载后,About 组件会被单独打包成 About.js
:
// 使用懒加载:只有访问 /about 时才加载 About 组件代码
const routes = [
{
path: '/about',
component: () => import('../views/About.vue') // 动态 import
}
]
路由懒加载的优势
- 减小初始加载体积:只加载首页必需的代码,提升首屏加载速度。
- 节省带宽资源:用户未访问的页面代码不会被下载。
- 优化用户体验:更快的首屏加载速度,减少用户等待时间。
在 Vue Router 中的实现
Vue Router 官方推荐使用动态 import()
实现路由懒加载,语法如下:
// 路由配置文件(router/index.js)
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/HomeView.vue') // 懒加载 Home 组件
},
{
path: '/about',
name: 'About',
// 注释说明懒加载的效果
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
- 其中
/* webpackChunkName: "about" */
是可选的魔法注释,用于指定打包后的代码块名称(方便调试)。 - 打包后会生成类似
about.[hash].js
的独立文件,仅在访问/about
时加载。
点击关于的时候,再加载
总结
路由懒加载是通过动态导入语法,将路由组件代码分割成多个小块,按需加载的技术。它能有效优化单页应用的加载性能,是大型 Vue 项目的必备优化手段。核心就是 “不用不加载,用的时候再加载”。