从Vue2 到 Vue3,这些路由差异你需要掌握!
前言
Vue3
已经出来很长时间了,但对Vue3
的路由却了解的非常少,甚至只知道最基本的跳转和参数获取,这些技能处理一些复杂的功能是不够的,最近把Vue3
的路由版本差异给学习了一下,在这里把结果分享给大家!!!
一、导航守卫
全局前置守卫
全局前置守卫通常用来做权限控制,使用 router.beforeEach
即可添加:
const router = createRouter({...})
router.beforeEach((to, from) => {
// ...
// 返回 false 取消导航
return false;
})
每个守卫方法接收两个参数:
to
::即将进入的目标路由from
:当前正要离开的路由 返回值可以是以下值:false
:取消当前导航true | undefined
:调用下一个守卫- 一个路由地址:字符串或对象。表示中断当前导航,进行下一个新的导航
router.beforeEach(async(to, from) => {
// 检查用户是否已经登录
if (!isAuthenticated && to.name !== 'Login) {
return { name: 'Login' }
}
})
注意:在全局前置守卫中,要确保 next
在导航守卫中只被调用一次。
全局解析守卫
router.beforeResolve
用法和 router.beforeEach
类似。它是在导航被确认之前,所有组件内守卫和异步路由组件被解析之后被调用。
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// 处理错误,然后取消导航
return false
} else {
// 取消导航并把错误传给全局处理器
throw error
}
}
}
})
上面这个例子,是为了确保用户可以访问自定义 meta 属性。
全局后置钩子
router.afterEach
和守卫不通,全局后置钩子不接受 next
函数,也不能跳转到其他的路由地址。但是它对于访问分析、更改页面标题、生命页面等辅助功能很有帮助。
router.afterEach((to, from) => {
sendTo(to.fullPath)
})
二、路由模式
Vue3 中不再使用 new Router()
创建 router,而是调用 createRouter
方法:
import { createRouter } from 'vue-router'
const router = createRouter({
// ...
})
路由模式 mode
配置为 history
,属性值调整为:
- ‘history’ =>
createWebHistory()
-
- ‘hash’ =>
createWebHashHistory()
- ‘hash’ =>
-
- ‘abstract’ =>
createMemoryHistory()
- ‘abstract’ =>
import { createRouter, createWebHistory } from 'vue-router'
// createWebHashHistory 和 createMemoryHistory (SSR相关) 同理
createRouter({
history: createWebHistory(),
routes: []
})
基础路径 base
被作为 createWebHistory
的第一个参数进行传递(其他路由模式也是一样):
import { createRouter, createWebHistory } from 'vue-router'
createRouter({
history: createWebHistory('/base-url/'),
routes: []
})
三、路由组件传参
当我们获取路由参数时,通常在模板中使用 $route
,在逻辑中调用 useRoute()
方法,如:
<template>
<div>User {{ $route.params.id }}</div>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id)
</script>
以上方法比较麻烦,而且与路由紧密耦合,不利于组件封装。我们可以在创建路由时通过 props
配置来解除这种行为:
const routes = [
{
path: '/user/:id',
name: 'user',
component: User,
props: true
}
]
此时 route.params
将直接被设置为组件的 props
,这样组件就和路由参数解耦了:
<template>
<div>User {{ id }}</div>
</template>
<script setup lang="ts">
const props = defineProps<{
id: string
}>()
console.log(props.id)
</script>
布尔模式
当 props
设置为 true
时,route.params
将被设置为组件的 props
。
命名视图
对于有命名视图的路由,你必须为每个命名视图定义 props
配置:
const routes = [
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
对象模式
当 props
是一个对象时,它会将此对象设置为组件 props
。当 props
是静态的时候很有用。
const routes = [
{
path: '/user',
component: User,
props: { newsletterPopup: false }
}
]
函数模式
我们也可以创建一个返回 props
的函数。这允许你将参数转换为其他类型:
const routes = [
{
path: '/user',
component: User,
props: route => ({ id: route.query.id })
}
]
如 /user?id=123
参数会被转为 { id: '123' }
作为 props
传给组件。
四、滚动行为
我们可以通过 vue-router
自定义路由切换时页面如何滚动。比如,当跳转到新路由时,页面滚动到某个位置;切换路由时页面回到之前的滚动位置。
当创建路由实例时,我们只需要提供一个 scrollBehavior
方法:
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
scrollBehavior
函数接收 to
和 from
路由对象。第三个参数 savedPosition
,只有当这是一个 popstate
导航时才可用(点击浏览器的后退/前进按钮,或者调用 router.go()
方法)
滚动到固定距离
该函数可以返回一个 ScrollToOptions
位置对象:
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// 始终滚动到顶部
return { top: 0 }
},
})
滚动到元素位置
可以通过 el
传递一个 CSS
选择器或一个 DOM
元素。在这种情况下,top
和 left
将被视为该元素的相对偏移量。
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// 始终在元素 #main 上方滚动 10px
return {
// el: document.getElementById('main'),
el: '#main',
top: -10,
}
},
})
滚动到锚点位置
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
el: to.hash,
}
}
},
})
滚动到之前的位置
在按下浏览器 后退/前进
按钮,或者调用 router.go()
方法时,页面会回到之前的滚动位置:
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
})
注意:如果返回一个 false
的值,或者是一个空对象
,则不会发生滚动。我们还可以在返回的对象中添加 behavior: 'smooth'
,让滚动更加丝滑。
延迟滚动
有时候我们不希望立即执行滚动行为。例如当页面做了过渡动效,我们希望过渡结束后再执行滚动。要做到这一点,我们可以返回一个 Promise
:
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ left: 0, top: 0 })
}, 500)
})
}
})
五、过渡动效
基本用法
如果想要在路由组件上使用转场,对导航进行动画处理,我可以使用 v-slot
结合 Animete.css
来实现:
<RouterView v-slot="{ Component }">
<transition enter-active-class="animate__animated animate__fadeIn">
<component :is="Component" />
</transition>
</RouterView>
单个路由的过渡
上面的用法会对所有的路由使用相同的过渡。如果你想让每个路由的组件有不同的过渡,可以将 元信息
和动态的 enter-active-class
结合在一起,放在 <transition>
上:
// js
const routes = [
{
path: '/home',
component: Home,
meta: { transition: 'animate__fadeIn' },
},
{
path: '/user',
component: User,
meta: { transition: 'animate__bounceIn' },
},
]
// vue
<RouterView v-slot="{ Component }">
<transition :enter-active-class="`animate__animated ${$route.meta.transition}`">
<component :is="Component" />
</transition>
</RouterView>
复用的组件之间进行过渡
const routes = [
{
path: '/user/:id',
component: User,
meta: { transition: 'animate__bounceIn' },
},
]
定义以上路由,当从 /user/123
切换到 /user/456
时是没有任何过渡效果的。这时候我们可以添加一个 key
属性来强制进行过渡,key
值只要不同就行了。说白了就是让 Dom
不要被复用,和 v-for
的 key
属性原理刚好相反。
<router-view v-slot="{ Component, route }">
<transition :enter-active-class="`animate__animated ${$route.meta.transition}`">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
六、动态路由
添加路由
当我们做用户权限的时候,添加路由非常有用。可以使用 router.addRoute()
来添加一个路由:
router.addRoute({ path: '/about', name: 'about', component: About })
注意:跟之前版本不同的是,路由只能一个一个添加,不能批量添加。
删除路由
以下几个方法都可以删除路由:
- 通过使用
router.removeRoute()
按名称删除路由:
router.addRoute({ path: '/about', name: 'about', component: About })
// 删除路由
router.removeRoute('about')
- 通过添加一个名称相同的路由,替换掉之前的路由:
router.addRoute({ path: '/about', name: 'about', component: About })
// 这将会删除之前已经添加的路由,因为他们具有相同的名字且名字必须是唯一的
router.addRoute({ path: '/other', name: 'about', component: Other })
- 通过调用
router.addRoute()
返回的回调函数:
const removeRoute = router.addRoute(routeRecord)
removeRoute() // 删除路由如果存在的话
当路由没有名称时,这种方法非常有用。
添加嵌套路由
要将嵌套路由添加到现有的路由中,可以将路由的 name
作为第一个参数传递给 router.addRoute()
,这和通过 children
添加的效果一样:
router.addRoute({ name: 'admin', path: '/admin', component: Admin })
// 添加嵌套路由
router.addRoute('admin', { path: 'settings', component: AdminSettings })
相当于:
router.addRoute({
name: 'admin',
path: '/admin',
component: Admin,
children: [{ path: 'settings', component: AdminSettings }]
})
结论
本片文章大部分来自官网,也有一些来自自己的实践心得。希望对你的开发工作有所帮助。当然这并不是 Vue-Router4 的所有内容,比如还有路由匹配、重定向和别名等等,大家可以自行在官网查看。如果今天的分享对你有所帮助。
- Author: 未来可期
- Link: http://shansec.github.io/post/blog/%E4%BB%8EVue2-%E5%88%B0-Vue3%E8%BF%99%E4%BA%9B%E8%B7%AF%E7%94%B1%E5%B7%AE%E5%BC%82%E4%BD%A0%E9%9C%80%E8%A6%81%E6%8E%8C%E6%8F%A1/
- License: This work is under a 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. Kindly fulfill the requirements of the aforementioned License when adapting or creating a derivative of this work.