一,前言
1.Vue Router
是 Vue.js
的官方路由库,vue
全家桶成员之一
2.使用 npm install vue-router@4
安装
二,项目配置
在
vue
项目中,配置vue-router
分为两个步骤
(1)初始化路由对象
(2)挂载路由
1.初始化路由对象
(1)createRouter
用于初始化路由对象,接受一个选项对象作为参数
(2)初始化路由最重要的是配置routes
来确定整个项目的路由匹配规则
import { createRouter, createWebHashHistory } from "vue-router";
export default createRouter({
history: createWebHashHistory(),//hash模式
routes:[{ path: "/", component: Login }]//路由配置规则数组
})
2.挂载路由
(1)使用app.use()
将创建的路由对象挂载到全局中,
(2)挂载之后,我们可以在全局中使用$router
, $route
, <router-view/>
, <router-link/>
这些api
import { createApp } from 'vue'
import router from './router/index.js'
import App from './App.vue'
const app = createApp(App)
app.use(router)
三,路由匹配规则配置
1.在vue-router
中,路由匹配规则这个概念是非常重要,它表示了路由和组件的映射,通过routes
字段配置。
2.一个最基础的路由配置如下,path
表示路径,component
表示对应的组件
routes:[{
path: "/", //路径
component: Login, //匹配组件
}]
四,视图容器
1.vue-router
内置组件<router-view>
用于是用于承载路由跳转的容器,组件能展示出来,离不开<router-view>
2.注意,router-view
上的组件因路由匹配规则切换时,组件会经历卸载到挂载完整的生命周期。
通过项目配置,路由配置规则配置,和视图容器已经完成了最基本的路由跳转
五,视图容器的组织方式
1.视图的组织可以分两种,一种是嵌套关系,一种是平铺关系
2.这涉及到vue-router
知识点,路由嵌套和命名视图
六,路由嵌套
1.要实现路由嵌套,首先要配置相应的规则,vue-router
的路由嵌套关系,是通过children
实现的
routes: [
{
path: "/layout", //路径
component: Layout, //匹配组件
children: [
{ path: "A", component: A }, //只匹配/layout/A
{ path: "B", component: B }, //只匹配/layout/B
{ path: "/C", component: C }, //匹配/C 等同于/layout/C
],
},
];
2.在上面路由规则中,/layout
将会匹配到Layout
组件,设Layout
组件内部有一个router-view
,通过路由/layout/A
, 将会把A
组件渲染至内部的router-view
3.children
配置只是另一个路由数组,就像 routes
本身一样。因此,可以根据自己的需要,不断地嵌套视图。
4.注意以 /
开头的嵌套路径将被视为根路径。如果添加了/
我们可以直接通过/C
来访问到嵌套关系,而不必使用/layout/C
,换句话说/C
等于/layout/C
5.注意,当访问/layout
时,内部router-view
是不会有任何渲染的,因为匹配不到,假如我们想有一个默认视图,可以通过设置一个空路由
routes: [
{
path: "/layout",
component: Layout,
children: [
{ path: "", component: A }, //默认展示空路由
{ path: "B", component: B }, //只匹配/layout/B
],
},
];
七,命名视图
1.同路由嵌套,想实现布局关系,首先也要配置相应的规则,vue-router
的路由嵌套关系规则是通过components
实现的,注意后面的s
routes: [
{
path: "/layout",
components: {
default: Home,
leftSide: LeftSide,
rightSide: RightSide
},
},
];
2.之后我们通过属性name
为视图容器命名,来组织我们的布局结构
<div>
<router-view name="leftSide"/>
<router-view/> <!--没有name的默认名为default-->
<router-view name="rightSide"/>
</div>
3.当我们访问/layout
就能渲染出相应的结构
4.注意命名视图里的某个容器是不支持路由切换的,要想实现这个效果,可以借助vue
内置组件component
配合路由的不同的参数,来渲染不同的组件。
八,路由跳转
vue-router
提供了两种方式来触发路由跳转(1)声明式
(2)编程式
1.声明式
(1)声明式是以内置组件<router-link>
来进行跳转
2.编程式
(1)编程式是以api
形式进行跳转,api
被挂载在vue2
的$router
,和vue3
的useRouter
中
(2)编程式导航api
如下
api | 介绍 |
---|---|
push | router.push 会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL |
replace | router.replace 导航时不会向 history 添加新记录,而是取代了当前的条目,replace 调用后返回的是一个promise ,当成功跳转时执行.then ,跳转失败时执行.catch ,例如如果我们在导航守卫时拒绝页面跳转时,此时就会执行.catch |
back | router.back 用于返回上一条记录 |
forward | router.forward 用于前进到下一条记录 |
go | router.go 用于横跨历史,router.go(-1) 等于router.back() ,router.go(1) 等于router.forward() |
(3)其中使用频率最高的就是push
和replace
,push
和replace
使用方法相似,接受一个选项对象
push({
path:"/demo" //跳转路径
name:"demo" //对于具名路由,可以使用name字段来跳转
params:{id:"xxx"} //传递动态参数,注意params不能同时和path使用,要传递动态参数时,可以选择具名路由
query:{id:"xxx"} //传递查询参数,是url上?后的参数
...
})
(4)注意,当使用push
跳转相同页面时,vue-router
默认不会重复添加相同的路由,这导致改变参数也不能生效,比如说我们正处于一个user
页面,这时我们要在推一个携带不一样的参数user
页面,直接使用push
是推不进去的,解决方法如下
//跳转时携带时间参数
this.$router.push({
name: 'download',
query: {
type: this.checkedType,
label: this.checkedLabel,
t: Date.now()
}
})
//给视图容器加个key,让vue知道,这是不同组件
<router-view :key="$route.path + $route.query.t">
</router-view>
九,路由跳转传参
vue-router
路由跳转传递参数有两种(1)动态参数
params
(2)查询参数query
1.动态参数
(1)动态参数首先需要在路由规则配置时显式配置,在path
后使用:
routes:[
//这里name即为动态参数
{path:'/user/:name',component:User}
]
(2)对于编程式跳转方式,我们在push
和repalce
方法的params
里传递
import { useRouter } from 'vue'
const router = useRouter()
router.push({
path:'/user'
params:{ name:'LL'}
})
//最终形成的路由: /user/LL
(3)在跳转的页面上,我们可以通过$route.params
(vue2
)或useRoute.params
(vue3
)获取到传递的动态参数
$route.params //{name:lin}
//在setup中
import { useRoute } from 'vue-router'
const route = useRoute()
route.params //{name:lin}
2.查询参数
(1)不同于动态参数,查询参数的使用不需要配置,
(2)对于编程式跳转方式,我们在push
和repalce
方法的query
里传递
import { useRouter } from 'vue'
const router = useRouter()
router.push({
path:'/user'
query:{ name:'LL'}
})
//最终形成的路由: /user?name=LL
(3)在跳转的页面上,我们可以通过$route.query
(vue2
)或useRoute.query
(vue3
)获取到传递的动态参数
$route.query//{name:lin}
//在setup中
import { useRoute } from 'vue-router'
const route = useRoute()
route.query//{name:lin}
十,命名路由
1.在配置路由的时候,我们可以通过配置name
选项,来为路由命名。
routes:[{
path: "/login", //路径
name:'login',
component: Login, //组件
}]
2.配置了命名路由后,我们不仅可以通过path
来跳转,也可以用name
来跳转,如下,注意当使用动态参数传递时,对于编程式导航必须借助于命名路由进行跳转
router.push({ name: 'login'})
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用,否则会忽略params
router.push({ path: '/user', params: { username } }) // -> /user
<router-link :to="{ name: 'login'}">
Login
</router-link>
3.所有路由的命名都必须是唯一的。如果为多条路由添加相同的命名,路由器只会保留最后那一条
十一,路由守卫
1.在vue-router
中守卫可以分为3种
(1)全局守卫
(2)路由守卫
(3)组件守卫
2.全局守卫
(1)全局前置守卫:router.beforeEach
接受(
to
,from
,next
)参数 ,to
和from
是标准的路由对象
.
返回false
时,取消本次导航
返回true
或undefined
,则导航是有效的,并调用下一个导航守卫
返回一个标准的路由对象,相当于调router.push
,会中断当前的导航,同时用相同的from
创建一个新导航
router.beforeEach(async (to, from) => {
if(没有权限){ //取消本次导航
return false
}
if(有权限 && 还未获取token){ //重定向
return { name: 'Login' }
}else{ //导航有效
return true
}
(2)全局解析守卫:router.beforeResolve
和
router.beforeEach
类似,不同的是,解析守卫刚好会在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用
.
router.beforeResolve
是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置
.
总的来说没有什么特别的,就是触发的时机不一样,我们可以进行一些不同的操作
(3)全局后置守卫:router.afterEach
接受(
to
,from
)参数 ,注意不接受next
.
在afterEach
也无法在改变导航本身,可以用于修改页面标题,分析页面等等
3.路由守卫
(1)beforeEnter
在进入路由时触发,且注意只有由不同的路由进入才触发,当使用动态路由时,动态参数的改变不会触发
beforeEnter
function removeQueryParams(to) {
if (Object.keys(to.query).length)
return { path: to.path, query: {}, hash: to.hash }
}
function removeHash(to) {
if (to.hash) return { path: to.path, query: to.query, hash: '' }
}
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: [removeQueryParams, removeHash],
},
{
path: '/about',
component: UserDetails,
beforeEnter: [removeQueryParams],
},
]
4.组件守卫
(1)beforeRouteEnter
在渲染该组件的对应路由被验证前调用
不能获取组件实例this
因为当守卫执行时,组件实例还没被创建
.
该导航守卫接受第三个参数next
,且是唯一一个守卫next
接受一个参数作为回调函数,并把组件实例作为回调方法的参数
(2)beforeRouteUpdate
在当前路由改变,但是该组件被复用时调用
举例来说,对于一个带有动态参数的路径/users/:id
,在/users/1
和/users/2
之间跳转的时候,
由于会渲染同样的UserDetails
组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例this
(3)beforeRouteLeave
在导航离开渲染该组件的对应路由时调用,与
beforeRouteUpdate
一样,它可以访问组件实例this
.
这个 离开守卫 通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回false
来取消
5.各个守卫执行顺序
(1)导航被触发
(2)失活组件触发beforeRouteLeave
(3)全局前置守卫beforeEach
触发
(4)被复用组件触发beforeRouteUpdage
(5)在路由配置里调用beforeEnter
(6)解析异步路由组件
(7)被激活的组件触发beforeRouteEnter
(8)调用全局的 beforeResolve
(9)导航被确认
(10)触发后置守卫afterEach
(11)触发 DOM
更新。
(12)触发beforeRouteEnter
的传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
十二,路由配置优化
1.组件静态配置,当打包构建应用时,JavaScript
包会变得非常大,影响页面加载
import Login from './login.vue
createRouter {
history: createWebHashHistory(),//hash模式
routes:[{
path: "/", //路径
redirect:"/login", //重定向
component: Login, //组件
},]
}
2.组件动态配置:把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效
const Login = ()=>import('./index.vue')
createRouter {
history: createWebHashHistory(),//hash模式
routes:[{
path: "/", //路径
redirect:"/login", //重定向
component: Login, //组件
},]
}