Vue Router深入学习(二)
通过阅读文档,自己写一些demo来加深自己的理解。(主要针对Vue3) 上一篇:Vue Router深入学习(一)
1. 路由元信息
有时,你可能希望将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的meta 属性来实现,并且它可以在路由地址和导航守卫上都被访问到。定义路由的时候你可以这样配置 meta 字段
语法:
const routes = [
{
path: '/user',
component: () => import('../components/User.vue'),
meta: {
tag: '用户',
isLogin: true
}
}
];
1.1 简单使用
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/user',
component: () => import('../components/User.vue'),
meta: {
tag: '用户',
isLogin: true
}
}
];
export default new createRouter({
history: createWebHistory(),
routes
});
<template>
<h2>User</h2>
<p>{{ route.meta }}</p>
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.meta)
</script>
<style>
</style>
1.2 搭配路由守卫使用
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/user',
component: () => import('../components/User.vue'),
meta: {
tag: '用户',
isLogin: true
},
children: [
{
path: ':id(\\d+)',
component: () => import('../components/UserId.vue'),
meta: {
type: 'id',
requireAuth: true
}
},
{
path: ':name',
component: () => import('../components/UserName.vue'),
meta: {
type: 'name',
requireAuth: false
}
}
]
}
];
export default new createRouter({
history: createWebHistory(),
routes
});
路由前置守卫
router.beforeEach((to, from) => {
if (to.meta.requireAuth) {
return {
path: '/user',
query: {
redirect: to.path
}
}
}
})
2. 数据获取
有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。
2.1 导航完成后获取数据
当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
<template>
<div class="content">
<p>id: {{ post.id }}</p>
</div>
<span>
<router-link :to="{ name: 'Post', params: { id: 222 } }">222</router-link>
</span>
<span>
<router-link :to="{ name: 'Post', params: { id: 333 } }">333</router-link>
</span>
<span>
<router-link :to="{ name: 'Post', params: { id: 444 } }">444</router-link>
</span>
</template>
<script setup>
import { reactive, watchEffect } from 'vue'
import { useRoute } from 'vue-router'
let post = reactive({
id: null
})
const route = useRoute()
const fetchData = () => {
post.id = route.params.id
}
watchEffect(() => {
const id = post.id
fetchData()
})
</script>
<style scoped>
span {
margin: 0 5px;
}
</style>
2.2 在导航完成前获取数据
通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法
<template>
<div class="content">
<p>id: {{ post.id }}</p>
</div>
<span>
<router-link :to="{ name: 'Post', params: { id: 222 } }">222</router-link>
</span>
<span>
<router-link :to="{ name: 'Post', params: { id: 333 } }">333</router-link>
</span>
<span>
<router-link :to="{ name: 'Post', params: { id: 444 } }">444</router-link>
</span>
</template>
<script>
export default {
data() {
return {
post: {}
}
},
beforeRouteEnter(to, from, next) {
next(vm => {
vm.setData(to.params)
})
},
beforeRouteUpdate(to, from) {
this.post = to.params
},
methods: {
setData(post) {
this.post = post
}
}
}
</script>
<style scoped>
span {
margin: 0 5px;
}
</style>
效果和上图一样。这里有点问题,通过beforeRouteEnter 无法获取到setup 里的函数、数据等,所以变成了使用Vue2的形式来实现。
3. 过渡动效
3.1 transition简单了解
<Transition> 会在一个元素或组件进入和离开 DOM 时应用动画
3.2 简单使用
<router-view v-slot="{ Component }">
<transition name="fade">
<component :is="Component"></component>
</transition>
</router-view>
在router-view上使用v-slot获取对应的组件,使用component动态组件来渲染这个组件,然后用transition包裹住这个动态组件
对应的路由组件只能有一个根元素,否则过渡将没有效果
.fade-enter-from,
.fade-leave-to {
transform: translateX(-100px);
opacity: 0;
}
.fade-enter-active {
transition: all 1s;
}
效果:
代码部分
<template>
<router-view v-slot="{ Component }">
<transition name="fade">
<component :is="Component"></component>
</transition>
</router-view>
</template>
<script setup>
</script>
<style>
.fade-enter-from,
.fade-leave-to {
transform: translateX(-100px);
opacity: 0;
}
.fade-enter-active {
transition: all 1s;
}
</style>
3.3 单个路由的过渡
原理很简单,路由配置时在meta上 添加上trasition 属性,再动态地和name 结合在一起就行
const routes = [
{
path: '/',
component: () => import('../components/Home.vue'),
},
{
path: '/post',
name: 'Post',
component: () => import('../components/Post.vue'),
meta: {
transition: 'slide-left'
}
},
{
path: '/user',
name: 'User',
component: () => import('../components/User.vue'),
meta: {
transition: 'slide-right'
}
}
];
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || fade">
<component :is="Component"></component>
</transition>
</router-view>
再添加上对应的css样式
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.slide-left-enter-active,
.slide-right-enter-active {
transition: all 1s;
}
.slide-left-enter-from,
.slide-left-leave-to {
transform: translateX(-200px);
}
.slide-right-enter-from,
.slide-right-leave-to {
transform: translateX(200px);
}
3.4 基于路由的动态过渡
根据目标路由和当前路由之间的关系,动态地确定使用的过渡
如:添加一个 全局后置钩子,根据路径的深度动态添加信息到 meta 字段
router.afterEach((to, from) => {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
to.meta.transition = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})
4. 滚动行为
在创建Router示例时,提供一个scrollBehavior 方法
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior (to, from, savedPosition) {
}
})
4.1 普通用法
const router = new createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
return {
top: 50
}
}
});
如果浏览器支持滚动行为,可以通过behavior 变得更流畅
const router = new createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
return {
top: 50,
behavior: 'smooth',
}
}
});
4.2 通过el 实现相对元素的偏移
el 可接受一个CSS选择器或一个DOM元素
const router = new createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
return {
el: 'h2',
top: 50
}
}
});
4.3 恢复之前的位置
返回 savedPosition ,在按下 后退/前进 按钮时,就会恢复之前的位置。像浏览器的原生表现那样
const router = new createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {
top: 0
}
}
}
});
4.4 延迟滚动
通过返回一个Promise来实现。
const router = new createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
top: 0,
behavior: 'smooth'
})
}, 500)
})
}
});
5. 路由懒加载
把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,会更高效
静态导入:
import User from '../components/User.vue'
const routes = [
{
path: '/user',
name: 'User',
component: User
}
];
动态导入:(实际上还省字)
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/user',
name: 'User',
component: () => import('../components/User.vue')
}
];
6. 动态路由
6.1 添加路由
路由配置:初始只有一个路由
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/',
component: () => import('../components/Home.vue')
}
];
const router = new createRouter({
history: createWebHistory(),
routes
});
export default router;
在导航守卫处添加新路由:实际上要限制那些页面的权限就可以这样添加,只有满足条件才会动态添加路由
router.beforeEach((to, from) => {
router.addRoute({
path: '/user',
name: 'User',
component: () => import('./components/User.vue')
})
})
6.2 删除路由
当路由被删除时,所有的别名和子路由都会被同时删掉
6.2.1 通过添加一个名字冲突的路由
如果添加与现有名称相同的路由,会先删除路由,再添加路由。
router.addRoute({ path: '/about', name: 'about', component: About })
router.addRoute({ path: '/other', name: 'about', component: Other })
6.2.2 通过调用 router.addRoute() 返回的回调
情境:路由没有名称,没法覆盖删除掉路由
const removeRoute = router.addRoute(routeRecord)
removeRoute()
6.2.3 通过使用 router.removeRoute() 按名称删除路由
router.addRoute({ path: '/about', name: 'about', component: About })
router.removeRoute('about')
6.3 添加嵌套路由
router.addRoute({
name: 'admin',
path: '/admin',
component: Admin,
children: [{ path: 'settings', component: AdminSettings }],
})
也可以将路由的name 作为第一个参数传递给router.addRoute() ,这样就可以有效的添加路由
router.addRoute({ name: 'admin', path: '/admin', component: Admin })
router.addRoute('admin', { path: 'settings', component: AdminSettings })
6.4 查看现有路由
|