Vue3-09 Vue Router
Vue3学习笔记-09 Vue Router
For Vue3
安装
1 | yarn add vue-router@4 |
创建路由实例
创建一个Hash模式的最简单路由并应用:
1 | import {createApp} from 'vue'; |
历史记录模式
使用createWebHashHistory()
创建Hash模式,使用createWebHistory()
创建HTML5模式,并应用在createRouter
的history
选项
推荐使用HTML5模式,但是需要在服务器上添加回退路由,配置实例参考官网。
要注意,如果使用HTML5模式,vue.config.js
中的publicPath
需要配置为绝对路径'/'
,不应该配置为相对路径,否则会出现资源找不到的情况!
路由匹配
在参数中自定义正则
在动态匹配路由时,如果有两个路径相同的动态路由,可以再括号中为参数指定自定义正则,例如为orderId
指定数字匹配:
1 | const routes = [ |
这样,/25
就会匹配第/:orderId
,其他情况会匹配/:productName
,路由的定义顺序不重要,因为自定义正则有着更高的优先级
要主要确保转义\
可重负参数
如果有需要匹配多个部分的路由,例如/first/second/third
,使用*
(0个或多个)和+
(一个或多个)将参数标记为可重复
1 | const routes = [ |
这将提供一个参数数组,使用命名路由时也需要传递数组。同时也可以通过在右括号天津嘉它们与自定义正则结合使用
1 | const routes = [ |
可选参数
使用?
来将一个参数标记为可选:
1 | const routes = [ |
命名视图
如果希望同级展示多个视图,在同一个组件中存在多个<router-view>
,这个时候就可以使用命名视图,例如:
1 | <router-view class="view left-sidebar" name="LeftSidebar"></router-view> |
在配置时,多个视图需要配置多个组件在components
选项上,key
值为<router-view>
的name
:
1 | const router = createRouter({ |
利用命名视图也可以创建嵌套视图的复杂布局,例如下面的例子:
1 | <!-- UserSettings.vue --> |
Nav
只是一个常规组件。UserSettings
是一个视图组件。UserEmailsSubscriptions
、UserProfile
、UserProfilePreview
是嵌套的视图组件。
配置时:
1 | { |
重定向和别名
重定向
重定向的目标除了路径和命名路由外,还可以是一个方法,这个方法接受目标路由作为参数,动态返回重定向目标
1 | const routes = [ |
注意,导航守卫并不会应用在跳转路由上,只会应用在其跳转的目标路由上
别名
alias
可以接收数组,可以接受绝对路径从而避免嵌套结构的限制,如果路由有参数,确保在别命中包含参数
1 | const routes = [ |
路由组件传参
把路由参数作为组件的Props传递以组件,避免组件与Route耦合,有三种模式:
(1)布尔模式
将props
设置为true
,route.params
将被设置为组件的Props
1 | const User = { |
对于命名视图,需要为每个视图定义Props配置:
1 | const routes = [ |
(2)对象模式
当props
是一个对象,会将对象中的属性设置为组件的Props,适合于Props为静态的情况
1 | const routes = [ |
(3)函数模式
props
可以是一个函数,接受当前路由作为参数,返回一个对象作为组件的Props
1 | const routes = [ |
导航守卫
导航守卫可以返回值:
false
,取消当前导航- 一个路由地址,就像调用
router.push
一样 - 抛出
Error
,取消导航,并调用router.onError
注册的回调 undefined
或true
,导航有效,调用下一个导航守卫
1 | router.beforeEach(async (to, from) => { |
next
是第三个参数,仍然被支持,但是根据上面的描述,完全可以不通过next
完成导航,使用next
经常会出现调用多次或者不被调用的问题,而通过返回值来确定导航是否有效会确保戴航始终有效
路由元信息
通过扩展RouteMeta
接口来输入meta
字段:
1 | // typings.d.ts or router.ts |
组合式API
在setup
中访问路由和当前路由
在setup
中不能使用this
,所以需要引入useRouter
和useRoute
函数代替this.$router
和this.$route
route
对象是一个响应式对象,属性都可以监听,但是应该避免监听整个route
对象以提升性能:
1 | import { useRoute } from 'vue-router' |
导航守卫
可以通过导入onBeforeRouteLeave
和onBeforeRouteUpdate
两个函数,来使用组件内的导航守卫。
可以用在任何由<router-view>
渲染的组件中,不必像组件内守卫那样直接用在路由组件上
过渡动效
需要使用v-slot
API
单个路由的过渡
可以将路由的元信息和动态name
结合在一起,放在<transition>
上
1 | const routes = [ |
1 | <router-view v-slot="{ Component, route }"> |
基于路由的动态过渡
可以根据目标路由和当前路由的关系,动态的确定使用过渡,需要利用afterEach
导航守卫,下面的例子是根据路径的深度动态添加transitionName
1 | router.afterEach((to, from) => { |
导航故障
检测导航故障
router.push
是一个异步方法,它会返回一个Promise,如果导航被阻止,用户停留在同一个页面上,router.push
返回的Promise
的解析值将是Navigation Failure,否则导航成功是,router.push
的返回结果是一个falsy值(通常是undefined
),这样来区分导航是否成功:
1 | const navigationResult = await router.push('/my-profile') |
Navigation Failure是一个带有额外属性的Error
实例,通过这个实例来判断哪些导航被阻止了及其原因,检查导航结果需要使用isNavigationFailure
和NavigationFailureType
1 | import { NavigationFailureType, isNavigationFailure } from 'vue-router' |
导航故障的fail
实例会暴露to
和from
属性,反应当前失败导航的当前位置和目标位置
鉴别导航故障
有不同的情况会导致导航终止,可以使用isNavigationFailure
和NavigationFailureType
来区分类型:
aborted
:在导航守卫中返回false
或者调用next(false)
中断了本次导航。cancelled
: 在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了router.push
。duplicated
:导航被阻止,因为我们已经在目标位置了。
isNavigationFailure
接受两个参数,第一个参数是Navigation Failure实例,第二个参数是决具体的导航失败的枚举值(通过NavigationFailureType
获取),如果不传入第二个参数,则只判断当前Error是不是Navigation Failure
NavigationFailureType
包含所有可能导航失败类型的枚举值,不要使用数组,永远只使用枚举值
检测重定向
重定向不会阻止导航,而是创建一个新的导航,可以读取路由地址的redirectedFrom
属性,判断重定向:
1 | await router.push('/my-profile') |
动态路由
通过addRoute
和removeRoute
来完成动态路由功能,但是这两个函数,只注册路由,如果新增路由与当前位置匹配,还需要调用router.push
或router.replace
来手动导航,才能显示该新路由
添加路由
只有一个路由的配置情况下:
1 | const router = createRouter({ |
进入任何页面都会显示Article
组件,在组件上为/about
添加一个新路由:
1 | router.addRoute({ path: '/about', component: About }) |
这时页面不会有任何改变,需要手动调用replace
方法来改变当前位置:
1 | router.addRoute({ path: '/about', component: About }) |
如果要等待新路由的完成,可以使用await router.replace()
在导航守卫中添加路由
在导航守卫中添加或删除路由,需要通过返回新的位置来触发重定向:
1 | router.beforeEach(to => { |
上面的例子实际上你是在替换要跳转的导航,在实际场景中,添加路由的欣慰更有可能是发生在导航之外,那么就不需要替换当前的导航了
添加嵌套路由
router.addRoute
除了添加单个路由之外,还可以将路由的name
作为第一个参数传递,这可以添加嵌套的路由,就像通过children
添加的一样
1 | router.addRoute({ name: 'admin', path: '/admin', component: Admin }) |
这等效于
1 | router.addRoute({ |
删除路由
当路由被删除时,所有的别名和子路由都会被删除。有几种方法来删除当前路由:
(1)添加一个名称相同的路由,那么就会删除之前的路由“
1 | router.addRoute({ path: '/about', name: 'about', component: About }) |
(2)如果是动态添加的路由,那么可以调用router.addRoute
1 | const removeRoute = router.addRoute(routeRecord) |
(3)使用router.removeRoute
按名称删除路由
1 | router.addRoute({ path: '/about', name: 'about', component: About }) |
为了避免名字冲突,可以使用Symbol
作为路由的名称
查看现有路由
router.hasRoute()
:确认是否存在指定名称的路由,接受类型为string
或者symbol
的参数router.getRoutes
:获取所有路由记录的完整列表
从Vue2迁移
创建路由实例的改变
new Router
变为createRouter
Vue Router不再是一个类,而是一组函数:
1 | // 以前是 |
路由模式改变
mode: 'history'
被history
配置替换:
"history"
:createWebHistory()
"hash"
:createWebHashHistory()
"abstract"
:createMemoryHistory()
,用于SSR的情况
1 | import { createRouter, createWebHistory } from 'vue-router' |
base
配置改变
base
作为createWebHistory
的第一个参数传递
路由配置
删除了通配符路由*
必须使用自定义的正则参数来定义全部路由
1 | {path: '/:pathMatch(.*)', component: NotFound} |
<keep-alive>
和<transition>
<keep-alive>
和<transition>
需要通过v-slot
API在<router-view>
内部使用
1 | <router-view v-slot="{ Component }"> |
细节还是挺多的,更详细的还是看文档吧。