Vue3的路由Router【7】
Vue3路由
- 1.概述:
- 2.组成部分:
- 1.路由模式(History Modes)
- 2.路由配置(Routes)
- 3.导航方式
- 声明式导航:
- 编程式导航:
- 3.与组件的交互:
- 3.路由的使用:
- 1.安装Vue Router库:
- 2.准备被路由的子组件:
- 3.路由配置文件准备:
- 相关说明:
- 导入路由库的核心内容:
- 路由对象配置内容:
- 被路由的组件的配置内容:
- 4.配置main.ts:
- 5.准备父组件App.vue:
- 相关说明:
- RouterLink标签:
- 概述及作用:等同于a标签的超链接
- 属性:
- RouterView标签:
- 6.测试结果展示:
- 4.路由的工作模式:
- 1.history:
- 2.hash
- 5.路由的懒加载:
- 1.概述:
- 2.实现:通过函数式动态加载
- 6.路由导航中to属性的两种写法:
- 写法1:字符串
- 写法2:对象
- 7.默认路由导航:
- 实现:
- 测试结果展示:
- 8.嵌套路由:
- 1.概述:
- 2.需求:
- 3.实现:
- 1.准备缺失的组件:
- 2.路由配置:
- 3.配置子组件的导航路径:
- 4.测试结果展示:
- 1.页面刚加载阶段:
- 2.点击打开Home组件:
- 3.点击打开news组件:
- 4.点击打开detail组件:
- 5.命名路由:
- 概述:
- 实现:
- 使用:
- 9.路由传参:
- 1.传参方式:
- 1.query传参:
- 需求:
- 实现:
- 1.准备数据:
- 2. 参数传递:
- 3.参数接收:
- 测试结果展示:
- 1.页面刚加载阶段:
- 2.参数传递之前:
- 3.点击传递参数:
- 存在问题:
- 解决方案:
- 示例:
- 测试:
- 2.params传参:
- 实现步骤:
- 测试:
- 2.参数传递过程简化:props
- 1.概述:
- 2.实现:
- 方式1:固定硬编码写法(仅作演示)
- 方式2:布尔配置(只能接收params参数)
- 方式3;函数式配置;(主要解决布尔配置无法接收query参数的问题)
- 10.导航的历史记录模式:
- 1.概述:
- 2.配置测试:
- push模式:不写则默认是push模式
- replace模式:
- 11.编程式导航:
- 1.概述:
- 2.实现:
- 12.路由的重定向:
- 1.概述:
- 2.使用:
- 需求:
- 实现:
- 字符串写法:
- 函数写法:
- 说明:
- 测试:
- 13.路由守卫:
- 1.概述:
- 2.分类:
- 1.全局守卫:针对于全部的路由组件
- 2.独享守卫:针对于某个组件
- 3.组件守卫:针对于组件内部
- 3.使用:
- 1.全局路由守卫配置:
- 1.配置全局前置守卫:
- 2.配置全局后置守卫:
- 2.独享路由守卫配置:
- 3.组件路由守卫配置:
1.概述:
在 Vue 3 中,路由(Router)用于实现单页应用(SPA)中的页面导航。它允许用户在不刷新整个页面的情况下,通过切换不同的 URL 路径来显示不同的组件,从而给用户一种在浏览多个页面的感觉。
官方介绍文档:https://router.vuejs.org/zh/introduction.html
2.组成部分:
1.路由模式(History Modes)
- Vue 3 的路由支持多种历史模式,其中常见的有两种:
createWebHistory
(HTML5 History 模式):这种模式下,路由的 URL 看起来和传统的多页网站的 URL 类似,例如http://example.com/home
。它利用了浏览器的History API
,当用户在浏览器的地址栏输入 URL 或者通过前进、后退按钮操作时,浏览器会触发相应的popstate
事件,Vue Router 可以根据这些事件来加载不同的组件。这种模式的优点是 URL 比较美观、符合用户对传统网站 URL 的认知,并且对搜索引擎优化(SEO)比较友好。但是它需要服务器端的配合,当用户直接访问某个子路径(如http://example.com/home
)时,服务器需要能够正确地返回index.html
文件,让 Vue 应用来处理后续的路由逻辑。createWebHashHistory
(Hash 模式):在这种模式下,URL 会带有一个#
符号,例如http://example.com/#/home
。所有的路由信息都包含在#
之后的部分,当#
后面的路径发生变化时,浏览器不会向服务器发送请求(因为浏览器将#
及后面的内容视为页面内部的锚点,不会引起页面的重新加载)。这种模式的优点是在不支持History API
的老旧浏览器中也能正常工作,并且服务器端不需要做特殊的配置。但是 URL 看起来不太美观,并且由于#
的存在,可能会在一些场景下(如分享链接、与其他系统集成时)引起误解。
2.路由配置(Routes)
路由配置是一个数组,每个元素定义了一条路由规则。例如
import { createRouter, createWebHistory } from 'vue-router';
import Home from './components/Home.vue';
import About from './components/About.vue';const router = createRouter({history: createWebHistory(),routes: [{path: '/home',component: Home},{path: '/about',component: About}]
});
在上述代码中:
path
属性定义了路由的路径,它是一个字符串,用于匹配浏览器地址栏中的 URL 路径。例如,当用户访问http://example.com/home
时,会匹配到{ path: '/home', component: Home }
这条路由。component
属性指定了与该路径对应的要显示的组件。在这里,当匹配到/home
路径时,会显示Home
组件。- 除了
path
和component
,还可以定义其他属性,如: name
:为路由命名,方便在代码中通过名称引用路由,例如在编程式导航中。{ path: '/home', name: 'home-route', component: Home }
。meta
:用于存储一些自定义的元数据,如路由是否需要鉴权等。{ path: '/admin', component: Admin, meta: { requiresAuth: true } }
。children
:用于定义子路由,实现嵌套路由。例如:
{path: '/parent',component: ParentComponent,children: [{path: 'child',component: ChildComponent}]
}
当访问`http://example.com/parent/child`时,会先加载`ParentComponent`,然后在`ParentComponent`内部的`<router - view>`标签中加载`ChildComponent`。
3.导航方式
声明式导航:
- 通过`<router - link>`组件来实现。例如:
<router - link to="/home">Home</router - link>
<router - link :to="{ name: 'about - route' }">About</router - link>
- 第一个
<router - link>
使用to
属性直接指定了路径,当用户点击这个链接时,会导航到/home
路径对应的组件。 - 第二个
<router - link>
使用了动态绑定:
to,并通过对象的形式指定了路由的名称(假设about - route
是之前定义的路由名称),这种方式更加灵活,特别是在需要传递参数或者根据变量来确定导航路径时非常有用。
编程式导航:
在 JavaScript 代码中进行路由导航。可以通过`router.push`、`router.replace`和`router.go`等方法来实现。
router.push
:用于向历史记录中添加一个新的记录,实现导航到新的路由。例如:
import { useRouter } from 'vue-router';
export default {setup() {const router = useRouter();const navigateToHome = () => {router.push('/home');};return {navigateToHome};}
};
在上述代码中,当调用`navigateToHome`函数时,会将用户导航到`/home`路径对应的组件,并且在浏览器的历史记录中会添加一个新的记录,用户可以通过浏览器的后退按钮返回之前的页面。
router.replace
:和router.push
类似,但是它不会在历史记录中添加新的记录,而是替换当前的历史记录。这在一些不希望用户通过后退按钮返回之前页面的场景下比较有用,例如登录成功后直接替换登录页面的历史记录。router.go
:用于在历史记录中前进或后退指定的步数。例如,router.go(-1)
会让用户后退一步,router.go(1)
会让用户前进一步。
3.与组件的交互:
- 在组件中,可以通过
useRouter
和useRoute
这两个函数来与路由进行交互。 useRouter
:用于获取路由实例,通过这个实例可以进行编程式导航等操作。例如,在组件的setup
函数中:
import { useRouter } from 'vue-router';
export default {setup() {const router = useRouter();const someFunction = () => {// 假设根据某个条件进行导航if (someCondition) {router.push('/new - path');}};return {someFunction};}
};
useRoute
:用于获取当前路由信息,包括当前的路径、路由参数、查询参数等。例如:
import { useRoute } from 'vue-router';
export default {setup() {const route = useRoute();console.log(route.params);console.log(route.query);return {};}
};
当路由包含参数(如/user/:id
中的id
)或者查询参数(如/search?q=vue
中的q
)时,可以通过useRoute
获取这些参数并在组件中进行相应的处理。
3.路由的使用:
1.安装Vue Router库:
npm install vue-router@4
2.准备被路由的子组件:
<template><h1>我是Home组件</h1>
</template><script setup lang="ts" name="Home"></script><style scoped></style>
<template><h1>我是About组件</h1>
</template><script setup lang="ts" name="About"></script><style scoped></style>
3.路由配置文件准备:
在项目src目录下创建router文件夹,再在此文件夹下创建index.ts文件,用于配置路由;
import {createRouter, createWebHistory} from 'vue-router'
//引入被路由的组件
import Home from "@/components/Home.vue";
import About from "@/components/About.vue";
const router = createRouter({history: createWebHistory(),routes: [{path: "/home", 路由的地址component: Home 被路由组件},{path: "/about",component: About}]
})
//暴露router
export default router;
相关说明:
导入路由库的核心内容:
-
createRouter():此函数用于创建路由对象;
-
createWebHistory()/createWebHashHistory():选择路由工作模式,使用一个即可;
路由对象配置内容:
-
history:表示路由工作模式;
-
routes:值为数组,表示配置的需要被路由的组件;
被路由的组件的配置内容:
path:表示此组件被在父组件App.vue中导航跳转时的路径,若是一级路由,则值需要用'/'开头;component:表示被路由的组件名;
4.配置main.ts:
将整个路由对象router配置到main.ts中
import { createApp } from 'vue'
import router from "./router/index";
import App from './App.vue'//通过 createApp 创建Vue实例 对象
const vm = createApp(App);vm.use(router);// 通过 mount 绑定页面的容器 id="app"
vm.mount('#app')
5.准备父组件App.vue:
在App.vue中配置页面跳转的路径,及相关样式;
<template>
<!--to属性类似于a标签中的href属性,实现页面组件的跳转写法1:字符串形式写法2:对象形式注意事项:如果是字符串写法,在跳转到多层嵌套的子组件时,必须要写全路径如果路径太长,则可以在目标组件的配置中添加name属性进行命名路径之后在to属性的值中直接写name属性的值即可--><RouterLink active-class="activeClass" to="/home">点击打开Home组件</RouterLink>
<!-- <RouterLink active-class="activeClass" to="/about">点击打开About组件</RouterLink>--><RouterLink active-class="activeClass" :to="{path:'/about' }">点击打开About组件</RouterLink><hr/><RouterView></RouterView><hr/>
</template>
<script lang="ts" setup name="App">
</script>
<style scoped>
.activeClass {text-decoration: none;color: green;font-size: 36px;}
</style>
相关说明:
RouterLink标签:
概述及作用:等同于a标签的超链接
`<router - link>`<font style="color:rgba(0, 0, 0, 0.85);">是 Vue Router 提供的一个组件,</font>**<font style="color:rgba(0, 0, 0, 0.85);">用于在 Vue 应用中实现声明式的路由导航</font>**<font style="color:rgba(0, 0, 0, 0.85);">。它会渲染成一个带有正确链接的 HTML 元素(通常是</font>`<a>`<font style="color:rgba(0, 0, 0, 0.85);">标签),当用户点击这个元素时,会触发路由的切换,从而在不刷新整个页面的情况下加载对应的组件:</font>
属性:
-
active-class:表示被选中时的样式类名;
-
to:此属性表示路由组件的全路径,与路由配置中的path属性的值保持一致,一般有字符串或对象两种写法;
RouterView标签:
此标签用于渲染路由组件的内容,如没有此标签则无法将路由的组件进行渲染展示;
6.测试结果展示:
测试1:页面刚加载
测试2:点击打开Home组件
测试3:点击打开About组件
4.路由的工作模式:
1.history:
-
优点:URL更加清晰,不带#,更加接近传统的网站URL
-
缺点:后期项目上线后需要服务器配合处理路径合法问题
-
history: createWebHistory(),
2.hash
-
优点:兼容性好。不需要服务器额外的处理
-
缺点:不好看 并且SEO优化方面相对较差
-
history: createWebHashHistory(),
5.路由的懒加载:
1.概述:
我们在上面案例的index.ts中是直接将被路由的组件直接导入进来,其实这种方式不太好。如果组件过多,一次性导进来但又不一定立即使用,因此我们可以通过懒加载的方式在其使用时再进行加载;
2.实现:通过函数式动态加载
import {createRouter, createWebHistory} from 'vue-router'const router = createRouter({history: createWebHistory(),routes: [{path: "/home", 路由的地址//动态导入component: () => import('../components/Home.vue')},{path: "/about",component: () => import('../components/About.vue')}]
})
6.路由导航中to属性的两种写法:
写法1:字符串
<RouterLink active-class="activeClass" to="/home">点击打开Home组件</RouterLink><RouterLink active-class="activeClass" to="/about">点击打开About组件</RouterLink>
写法2:对象
<RouterLink active-class="activeClass" :to="{path:'/home' }
">点击打开Home组件</RouterLink> <RouterLink active-class="activeClass" :to="{path:'/about' }
">点击打开About组件</RouterLink>
7.默认路由导航:
实现:
如果我们希望页面刚加载出来就默认导航到一个路由组件中,那么我们可以这样实现:
import {createRouter, createWebHistory} from 'vue-router'const router = createRouter({history: createWebHistory(),//配置默认路由导航routes: [{path: "/",component: () => import('../components/Home.vue')},{path: "/home", 路由的地址//动态导入component: () => import('../components/Home.vue')},{path: "/about",component: () => import('../components/About.vue')}]
})
测试结果展示:
通过测试结果发现,当页面刚加载出来时,就自动导航到了Home组件中了;
8.嵌套路由:
1.概述:
嵌套路由指在一个被路由的组件中又包含了子组件,以此形成嵌套的形式;
2.需求:
现在上述案例的基础上新增了这样的需求:在点击打开Home组件后,出现News组件和message组件,再点击News组件时,导航到detail组件中,最终渲染出detail组件的内容;
3.实现:
1.准备缺失的组件:
<template><h1>我是News组件</h1>
</template><script setup lang="ts" name="News"></script><style scoped></style>
<template><h1>我是Message组件</h1>
</template><script setup lang="ts" name="Message"></script><style scoped></style>
<template><h1>我是Detail组件</h1>
</template><script setup lang="ts" name="Detail"></script><style scoped></style>
2.路由配置:
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'
const router = createRouter({history: createWebHashHistory(),routes: [{path: "/about",component: () => import('@/components/About.vue')},{path: "/home",component: () => import('@/components/Home.vue'),//配置子路由组件children: [{//子路由组件的path属性的值不加'/'path: "message",component: () => import('@/components/Message.vue')},{path: "news",component: () => import('@/components/News.vue'),//子路由组件的子路由组件children: [{path: 'detail',component: () => import('@/components/Detail.vue')}]}]}]
})
//暴露路由对象
export default router;
说明:
- 配置子路由组件时通过children属性进行配置,值为对象数组,每一个对象都表示一个子路由组件;
- 配置子路由组件时,path属性的值不加’/';
3.配置子组件的导航路径:
<template><h2>我是Home组件</h2><RouterLink to="/home/news">点击打开news组件</RouterLink><br/><RouterLink to="/home/message">点击打开message组件</RouterLink><hr/><RouterView></RouterView><hr/>
</template><script lang="ts" setup name="Home"></script><style scoped></style>
<template><h2>我是News组件</h2><RouterLink to="/home/news/detail">点击打开detail组件</RouterLink><RouterView></RouterView>
</template><script lang="ts" setup name="News"></script><style scoped></style>
说明:
-
配置导航子路由路径时必须从以及路径(案例中的/home)开始;
-
每个组件中都需要有<RouterView>标签,用于渲染展示导航路由组件的内容;
4.测试结果展示:
1.页面刚加载阶段:

2.点击打开Home组件:
3.点击打开news组件:
4.点击打开detail组件:
5.命名路由:
概述:
在上面案例中我们导航子路由组件detail时,由于此组件已经被嵌套了三层了,因此在news组件中配置导航路径时就比较麻烦,因为要从一级路径开始;因此我们可以通过命名路由来简化导航目标路由的路径,方便了跳转与后期的传参;
实现:
只需要在目标路由组件配置时加上一个name属性,其值就表示我们的命名路由(注意不加’/'),之后我们在配置导航路径时就可以直接写我们定义的路由了;
{path: 'detail',name:'xq', //命名路由component: () => import('@/components/Detail.vue')}
使用:
使用to的对象写法 name:'命名路由的名字'
<RouterLink :to="{name:'xq'}">点击打开详情组件</RouterLink>
9.路由传参:
1.传参方式:
1.query传参:
需求:
现在我们需要在news组件中准备一些数据传输到detail组件中,并在detail组件中进行渲染展示,同时将接收到的数据输出到控制台上;
实现:
1.准备数据:
<template><h2>我是News组件</h2><RouterLink to="/home/news/detail">点击打开detail组件</RouterLink><RouterView></RouterView>
</template><script lang="ts" setup name="News">
import {reactive} from "vue";
import {nanoid} from "nanoid";let newsList = reactive([{id: nanoid(), title: '新闻标题A', value: '这是一段新闻内容A'},{id: nanoid(), title: '新闻标题B', value: '这是一段新闻内容B'},{id: nanoid(), title: '新闻标题C', value: '这是一段新闻内容C'},{id: nanoid(), title: '新闻标题D', value: '这是一段新闻内容D'},{id: nanoid(), title: '新闻标题E', value: '这是一段新闻内容E'}
])
</script><style scoped></style>
2. 参数传递:
<template><h2>我是News组件</h2><RouterLink to="/home/news/detail">点击打开detail组件</RouterLink>传参<ul>//遍历对象数组<li v-for="(news,index) in newsList" :key="news.id">//to属性的字符串写法:将固定参数写到导航路由的路径上传递到目标组件中<!-- <RouterLink to="/home/news/detail?id=1&title=测试数据&value=测试内容">{{ news.title }}</RouterLink>-->//to属性的模板字符串写法:配合el表达式动态循环获取对象数组中的数据再将其写道导航路由的路径中传递到目标组件中<RouterLink :to="`/home/news/detail?id=${news.id}&title=${news.title}&value=${news.value}`">{{ news.title }}</RouterLink>//to属性的对象写法:属性1:目标路由的命名路由名,属性2:query对象中包裹传输的数据<!-- <RouterLink :to="{name:'xq',query:{id:news.id,title:news.title,value:news.value}}">{{ news.title }}</RouterLink>--></li></ul><hr/><RouterView></RouterView><hr/>
</template><script lang="ts" setup name="News">
import {reactive} from "vue";
import {nanoid} from "nanoid";let newsList = reactive([{id: nanoid(), title: '新闻标题A', value: '这是一段新闻内容A'},{id: nanoid(), title: '新闻标题B', value: '这是一段新闻内容B'},{id: nanoid(), title: '新闻标题C', value: '这是一段新闻内容C'},{id: nanoid(), title: '新闻标题D', value: '这是一段新闻内容D'},{id: nanoid(), title: '新闻标题E', value: '这是一段新闻内容E'}
])
</script><style scoped></style>
3.参数接收:
<template><h2>我是detail组件</h2>
<!-- 收到了参数:<br/>{{route.query.id}}<br/>{{route.query.title}}<br/>{{route.query.value}}-->
</template><script lang="ts" setup name="Detail">
//接收 路由传入的参数
import {useRoute} from "vue-router";
import {watch} from "vue";
//创建本次数据传输的路由对象,
//注意与index.ts中的router对象区分
const route = useRoute();console.log(route.query.id)
console.log(route.query.title)
console.log(route.query.value)</script><style scoped></style>
测试结果展示:
1.页面刚加载阶段:

2.参数传递之前:
3.点击传递参数:
存在问题:
在上面测试结果中可以看出,当我们多次传参时,虽然传递的参数可以被正常接收并渲染展示,但再控制台上只输出第一次传递的参数;这是因为<font style="color:rgba(0, 0, 0, 0.85);">在脚本部分的</font>`console.log`<font style="color:rgba(0, 0, 0, 0.85);">语句只在组件初始化时执行一次。当路由参数后续发生变化时,这些</font>`console.log`<font style="color:rgba(0, 0, 0, 0.85);">语句不会再次执行,所以控制台不会打印出新的参数值;</font>
解决方案:
示例:
为了在控制台中打印出每次路由参数变化的值,可以使用watch
来监听路由参数的变化。
<template><h2>我是detail组件</h2>收到了参数:<br/>{{route.query.id}}<br/>{{route.query.title}}<br/>{{route.query.value}}
</template><script lang="ts" setup name="Detail">
//接收 路由传入的参数
import {useRoute} from "vue-router";
import {watch} from "vue";
const route = useRoute();
watch(()=>route.query,(newValue)=>{console.log('id:',newValue.id)console.log('title:',newValue.title)console.log('value:',newValue.value)
})</script><style scoped></style>
测试:
通过测试结果可以看出,此时就可以将每次传递的数据在控制台进行打印输出了;
2.params传参:
实现步骤:
1.修改路由组件配置,接收数据的组件上配置参数占位符
{path: "detail/:id/:title/:value",name:'xq',component: () => import('../components/detail.vue')}
2.news组件修改参数传递方式:
//对象式写法(目标路由组件必须有命名属性(name属性))<RouterLink :to="{name:'xq',params:{id:news.id,title:news.title,value:news.value}}">{{ news.title }}</RouterLink>
//字符串写法(不常用)
<RouterLink :to="`/home/news/detail/${news.id}/${news.title}/${news.value}`">{{ news.title }}</RouterLink>
3.detail组件修改接收参数的方式:
<template><h2>我是detail组件</h2>收到了参数:<br/>{{route.params.id}}<br/>{{route.params.title}}<br/>{{route.params.value}}
</template><script lang="ts" setup name="Detail">
//接收 路由传入的参数
import {useRoute} from "vue-router";
import {watch} from "vue";
const route = useRoute();watch(()=>route.params,(newValue)=>{console.log('id:',newValue.id)console.log('title:',newValue.title)console.log('value:',newValue.value)
})</script><style scoped></style>
测试:
2.参数传递过程简化:props
1.概述:
在上述案例中在目标组件接收参数时,总需要写很多代码,如果参数太多,就会造成代码冗余,因此我们可以通过props属性实现参数接收的简化;
2.实现:
方式1:固定硬编码写法(仅作演示)
1.配置路由组件
{path: "detail/:id/:title/:value",name:'xq',component: () => import('../components/detail.vue'),props:{id:'001',title:'测试数据',value:'测试值'}}
2.news组件传输数据
<RouterLink :to="{name:'xq',params:{id:news.id,title:news.title,value:news.value}}">{{ news.title }}</RouterLink>
3.detail组件修改参数接收方式
<template><h2>我是detail组件</h2>收到了参数:<br />{{ id }}<br />{{ title }}<br />{{ value }}
</template><script lang="ts" setup name="Detail">
// 接收路由传入的参数
import { useRoute } from "vue-router";
import { defineProps, watch } from "vue";const props = defineProps(['id', 'title', 'value']);
console.log(props.id, props.title, props.value);
// 使用watch来监视props的变化
watch(() => props,(newProps, oldProps) => {console.log('props更新了:', newProps.id, newProps.title, newProps.value);// 这里也可以对比新旧props的值,比如:// if (newProps.id!== oldProps.id) {// console.log('id属性发生了变化,旧值:', oldProps.id, ',新值:', newProps.id);// }},{ deep: true }
);
</script><style scoped></style>
测试:
由于参数是固定值,所以在detail组件中接收的参数不发生变化;
方式2:布尔配置(只能接收params参数)
1.配置路由组件:
{path: "detail/:id/:title/:value",name:'xq',component: () => import('../components/detail.vue'),props:true}
2.news组件传递参数:
<RouterLink :to="{name:'xq',params:{id:news.id,title:news.title,value:news.value}}">{{ news.title }}</RouterLink>
3.deatil组件接收参数:
<template><h2>我是detail组件</h2>收到了参数:<br />{{ id }}<br />{{ title }}<br />{{ value }}
</template><script lang="ts" setup name="Detail">
// 接收路由传入的参数
import { useRoute } from "vue-router";
import { defineProps, watch } from "vue";const props = defineProps(['id', 'title', 'value']);
console.log(props.id, props.title, props.value);
// 使用watch来监视props的变化
watch(() => props,(newProps, oldProps) => {console.log('props更新了:', newProps.id, newProps.title, newProps.value);},{ deep: true }
);
</script><style scoped></style>
测试:
方式3;函数式配置;(主要解决布尔配置无法接收query参数的问题)
1.配置路由组件:如果接收params参数,则path属性上也需要同时添加参数占位符
{path: "detail",name:'xq',component: () => import('../components/detail.vue'),props(route){return route.query;}}
2.修改参数传递方式为query
<RouterLink :to="{name:'xq',query:{id:news.id,title:news.title,value:news.value}}">{{ news.title }}</RouterLink>
3.detail组件接收参数:
<template><h2>我是detail组件</h2>收到了参数:<br />{{ id }}<br />{{ title }}<br />{{ value }}
</template><script lang="ts" setup name="Detail">
// 接收路由传入的参数
import { useRoute } from "vue-router";
import { defineProps, watch } from "vue";const props = defineProps(['id', 'title', 'value']);
console.log(props.id, props.title, props.value);
// 使用watch来监视props的变化
watch(() => props,(newProps, oldProps) => {console.log('props更新了:', newProps.id, newProps.title, newProps.value);},{ deep: true }
);
</script><style scoped></style>
测试:
10.导航的历史记录模式:
1.概述:
在vue3中的路由中,有两种历史记录模式,分别是push追加,以及repalce替换,它们在组件的导航标签中进行配置,默认为push;
2.配置测试:
push模式:不写则默认是push模式
<RouterLink push :to="{name:'xq',params:{id:news.id,title:news.title,value:news.value}}">{{ news.title }}</RouterLink>
测试:
此时我们位于news组件
此时我们点击新闻标题A,跳转到detail组件;
此时,我们进行后退操作,观察页面跳转情况;
可以看出,页面又重新跳转到了news组件,说明我们从news到detail的跳转操作的历史记录是保存了的;
replace模式:
<RouterLink replace :to="{name:'xq',params:{id:news.id,title:news.title,value:news.value}}">{{ news.title }}</RouterLink>
测试:
此时我们位于news组件
此时我们点击新闻标题A,跳转到detail组件;
此时,我们进行后退操作,观察页面跳转情况;
此时我们发现后退之后并没有从detail组件跳到news组件,而是直接跳到了home组件,这也就说明,之前从news跳转到detail组件的历史记录替换了从home跳转到news的历史记录(而非追加),因此当我们后退时就直接回退到了home组件;
11.编程式导航:
1.概述:
在上面所有的案例中,我们都是通过标签实现组件的导航跳转,即将导航的路径或携带的参数都写到了标签的to属性中,这种导航属于声明式导航。
而编程式导航,是在事件的回调函数中进行定义导航的路径及参数的携带;
2.实现:
1.组件的导航方式定义:
<template><h1>我是News组件</h1><ul><li v-for="(news,index) in newsList" :key="news.id"><button @click="goDetail(news.id,news.title,news.value)">{{ news.title }}</button></li></ul><!-- 展示被路由的组件 --><RouterView></RouterView>
</template><script setup lang="ts" name="News">
import {reactive} from "vue";
import {nanoid} from "nanoid";
import {useRouter} from "vue-router";let newsList = reactive([{id: nanoid(), title: '新闻标题A', value: '这是一段新闻内容A'},{id: nanoid(), title: '新闻标题B', value: '这是一段新闻内容B'},{id: nanoid(), title: '新闻标题C', value: '这是一段新闻内容C'},{id: nanoid(), title: '新闻标题D', value: '这是一段新闻内容D'},{id: nanoid(), title: '新闻标题E', value: '这是一段新闻内容E'}
])
//获取路由对象
const router = useRouter();// router.replace() 替换历史记录
// router.push() 追加历史记录
/*router.push({// path?? 路径// name?? 命名路由// query?? 参数// params?? 参数
})*/
const goDetail = (id, title, value) => {router.push({name: 'xq',params: {id: id,title: title,value: value}})
}
</script><style scoped></style>
说明:
-
通过单击鼠标事件的回调函数实现导航跳转;
-
需要获取整个路由对象router;
-
在回调函数中,通过router选择历史记录模式(push/replace),在对应的方法中选择传递方式(path/name),参数类型(query/params)以及参数内容等;
2.detail组件接收数据:
<template><h2>我是detail组件</h2>收到了参数:<br />{{ id }}<br />{{ title }}<br />{{ value }}
</template><script lang="ts" setup name="Detail">
// 接收路由传入的参数
import { useRoute } from "vue-router";
import { defineProps, watch } from "vue";const props = defineProps(['id', 'title', 'value']);
console.log(props.id, props.title, props.value);
// 使用watch来监视props的变化
watch(() => props,(newProps, oldProps) => {console.log('props更新了:', newProps.id, newProps.title, newProps.value);},{ deep: true }
);
</script><style scoped></style>
3.测试结果展示:
12.路由的重定向:
1.概述:
路由的重定向指的是通过设置重定向的路径,使得当前组件重新跳转到另一组件中,我们前面写过一个需求:当页面刚加载出来时,默认跳转到某一个组件中。之前我们是通过{path;'/',components:目标组件名}的方式实现的,实际上我们通过重定向可以更加简单的实现该需求;
2.使用:
需求:
通过重定向的方式实现当页面刚加载出来时,就自动跳转到news组件
实现:
字符串写法:
import {createRouter, createWebHistory,createWebHashHistory} from 'vue-router'
import Home from '../components/Home.vue'
import About from '../components/About.vue'const router = createRouter({history: createWebHistory(),//history: createWebHashHistory(),routes: [//默认打开Home组件{path: "/",redirect:'/home'},{path: "/home",component: Home,redirect:'/home/news',children: [{path: "news",component: () => import('../components/news.vue'),children: [{path: "detail",name:'xq',component: () => import('../components/detail.vue'),props(route){return route.queyr;}}]},{path: "message",component: () => import('../components/message.vue')}]},{path: "/about",component: About}]
})
export default router;
函数写法:
import {createRouter, createWebHistory,createWebHashHistory} from 'vue-router'
import Home from '../components/Home.vue'
import About from '../components/About.vue'const router = createRouter({history: createWebHistory(),//history: createWebHashHistory(),routes: [//默认打开Home组件{path: "/",redirect:(to)=>{console.log(to);return{path:'/home'}}},{path: "/home",component: Home,redirect:(to)=>{console.log(to);return{path: '/home/news'}},children: [{path: "news",component: () => import('../components/news.vue'),children: [{path: "detail/:id/:title/:value",name:'xq',component: () => import('../components/detail.vue'),props(route){return route.params;}}]},{path: "message",component: () => import('../components/message.vue')}]},{path: "/about",component: About}]
})
export default router;
说明:
-
字符串写法适合比较简单的重定向场景,无法携带参数,只是明确说明目标组件的路径;
-
函数式写法更加灵活,可选择目标组件的路径(path)或命名路由(name)进行重定向,同时含可携带参数(query/params)到目标组件;
-
上述案例中重定向的函数式写法中的参数to表示访问当前组件的详细信息;
测试:
13.路由守卫:
1.概述:
路由守卫是 Vue Router 提供的一种机制,用于在路由跳转的不同阶段进行拦截和控制。它就像是一个 “关卡”,可以检查用户是否满足访问某个路由的条件,例如用户是否登录、是否具有访问权限等。如果条件不满足,路由守卫可以阻止路由跳转或者将用户重定向到其他页面。
2.分类:
1.全局守卫:针对于全部的路由组件
-
前置守卫:在跳转到目标路由组件之前
-
后置守卫:离开当前目标路由之前
2.独享守卫:针对于某个组件
3.组件守卫:针对于组件内部
3.使用:
1.全局路由守卫配置:
1.配置全局前置守卫:
//全局前置 路由守卫
router.beforeEach((to, from, next) => {console.log("执行了全局前置路由守卫:", to, from)next();//放行// /!* if (to.fullPath.includes("login")) {// if (sessionStorage.getItem('') != undefined) {// next();// }// }*!/// next();
})
说明:上述案例中配置了全局前置守卫,在beforeEach函数中有两个参数:to表示目标路由组件,from表示从那个组件过来;注意在函数中有个重要函数next(),调用此函数表示放行通过,否则就无法跳转,因此在这里可以进行一些判断,决定是否要放行;
测试:
拦截:
放行:
2.配置全局后置守卫:
router.afterEach((to, from) => {console.log("全局路由后置守卫", to, from)
})
说明:全局后置守卫(afterEach
)在路由跳转完成后被调用,其主要目的是用于记录访问历史、分析用户行为等操作,因此一般在后置守卫中不做拦截处理;
测试:
2.独享路由守卫配置:
{path: "message",component: () => import('@/components/Message.vue'),beforeEnter: (to, from, next) => {console.log("独享路由守卫", to, from)//next();}},
说明:上述案例中我们针对message组件配置了独享守卫,其作用是在跳转到该组件上时进行拦截;
测试:
放行:
拦截:
3.组件路由守卫配置:
在组件路由守卫中有三个可用的API:beforeRouteEntry,beforeRouteUpdate,beforeRouteLeave,其中beforeRouteEntry不可再setup语法糖中使用(如果是setup函数的普通写法,则可在与setup函数平级的地方调用),原因时此API与setup函数功能重复;
onBeforeRouteUpdate((to, from, next) => {/!*** // 在当前路由改变,但是该组件被复用时调用* // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,* // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。* // 因为在这种情况发生的时候,组件已经挂载好了,*!/console.log("onBeforeRouteUpdate:",to,from)next(); //放行
}) onBeforeRouteLeave((to, from, next)=>{// 在导航离开渲染该组件的对应路由时调用console.log("onBeforeRouteLeave: ",to, from)next();// 放行
})
测试:
正常放行:
离开时拦截: