Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。随着vue3发布,Vue Router也更新到了的4.x版本来与vue3的新特性做适配。本文基于Vue Router4。
Vue Router
1. 安装及配置
执行 npm install vue-router@4
。执行命令安装完成之后,在目录下创建 src/router/index.js 写入下面的配置:
import { createRouter, createWebHashHistory } from 'vue-router'
import Layout from '@/layout/index.vue'
import Home from '@/views/home/Home.vue'
const routes = [
{
path: '/',
component: Layout,
children: [{ path: '', component: Home }],
},
]
export default createRouter({
history: createWebHashHistory(),
routes,
})
复制代码
main.js中使用
// ...+
import router from './router/index'
createApp(App).use(router).mount('#app')
复制代码
tips
: hash模式使用 createWebHashHistory
方法, history模式使用 createWebHistory
方法。
2. 动态路由匹配
- 路由配置
{ path: '/courseinfo/:title', component: CourseInfo }
复制代码
- 两种跳转方式
<router-link :to="{ path: '/courseinfo/c' }">
跳转到c语言详情页
</router-link>
复制代码
import { useRouter } from 'vue-router';
const router = useRouter();
function toInfo() {
router.push({ path: '/courseinfo/python' })
}
复制代码
- 获取参数的方式
$route.params.title
只能在模板中使用 或useRoute().params.title
必须在setup中调用。因为setup
中不能使用this,所以$route
只能在模板中使用。 - 响应参数变化: 使用带有参数的路由时需要注意的是,当用户从
/courseinfo/javascript
导航到/courseinfo/python
时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用。解决方法:watch$route
对象上的任意属性,在这个场景中,就是$route.params
;或者使用导航守卫。 - 捕获所有路由或 404 Not found 路由
const routes = [
// 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
// 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
{ path: '/user-:afterUser(.*)', component: UserGeneric },
]
复制代码
3. 编程式导航
声明式 | 编程式 |
---|---|
<router-link :to="..."> | router.push(...) |
常见用法
// 传递path
router.push(`/user/${username}`)
// 或者
router.push({ path: `/user/${username}` })
复制代码
命名路由
路由定义时需要⼀个name,跳转时可根据name值跳转,router/index.js
{ path: '/courseinfo/:title', component: CourseInfo, name: 'CourseInfo' },
复制代码
导航时传参
params传参
// 使⽤`name`和`params`搭配,以利⽤⾃动的URL编码;不能使⽤`path`和`params`搭配
router.push({ name: 'user', params: { username } })
复制代码
query传参,跳转到user页,链接是 /user?data=123
,获取query参数 $route.query.data
router.push({ name: 'user', query: { data:123 } })
复制代码
router.replace
方法
跟 router.push
很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
声明式 | 编程式 |
---|---|
<router-link :to="..." replace> | router.replace(...) |
router.go(n)
方法
router.go(-1) // 返回、后退一步
复制代码
4. 路由守卫
全局守卫
- 范围最⼤,任何路由导航都会触发回调
const router = createRouter({ ... })
router.beforeEach((to, from) => {
// ...
// 返回 false 以取消导航
return false
})
复制代码
to
: 即将进入的目标 ,from
: 当前导航正要离开的路由
- 可选的第三个参数
next
示例:使用路由守卫进行登录权限验证,未登录跳转登录页,已登录跳转登录成功页。src/permission.js:
router.beforeEach((to, from, next) => {
if (to.path === '/loginsuccess') {
if (localStorage.getItem('isLogin')) {
next();
} else {
next({ path: '/login' });
}
} else {
next();
}
});
复制代码
根据localStorage
缓存判断,可以手动清除切换状态调试观察效果。
路由独享的守卫
你可以直接在路由配置上定义 beforeEnter
守卫:
// src/router/index.js
{
path: '/loginsuccess',
name: 'LoginSuccess',
component: LoginSuccess,
beforeEnter: (to, from) => {
console.log("----登录成功页面的路由独享的守卫")
}
},
复制代码
beforeEnter
守卫 只在进入路由时触发,不会在 params
、query
或 hash
改变时触发。例如,从 /users/2
进入到 /users/3
或者从 /users/2#info
进入到 /users/2#projects
。它们只有在 从一个不同的 路由导航时,才会被触发。
组件内路由守卫
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
const UserDetails = {
template: `...`,
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !
// 因为当守卫执行时,组件实例还没被创建!
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
// 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用
// 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
},
}
复制代码
在setup
中使用时,可以通过 onBeforeRouteUpdate
和 onBeforeRouteLeave
分别添加 update 和 leave 守卫。提示:setup
中没有提供beforeRouteEnter
的等效方法。
5. 路由元信息
定义路由配置时通过添加meta
字段来附加额外信息。src/permission.js 改写为 使用meta
路由元信息判断跳转登录成功页还是去登陆
router.beforeEach((to, from, next) => {
if (to.meta.auth) {
if (localStorage.getItem('isLogin')) {
next();
} else {
next({ path: '/login' });
}
} else {
next();
}
});
复制代码
6. 路由懒加载
打包时将单个路由组件分⽚打包,访问时才异步加载,可以有效降低app尺⼨和加载时间。 src/router/index.js
const Login = () => import('@/views/login/Login.vue');
const routes = [
{ path: '/login', name: 'Login', component: Login }
]
复制代码
7. composition api中使用
在组合式Api和setup
中无法访问this
,无法使用this.$router
等。所以新增了一些新的函数来访问router
route
及导航钩子
等。
- useRouter和useRoute
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
//...
</script>
复制代码
- 路由守卫钩子
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
// 等效于beforeRouteLeave,但是不能访问`this`
onBeforeRouteLeave((to, from) => {})
// 等效于beforeRouteUpdate,但是不能访问`this`
onBeforeRouteUpdate((to, from) => {})
</script>
复制代码
8. 动态路由
有时候需要在程序运行时添加或删除一些路由,这时,就可以用下面的方法。
- 新增路由
const TestRoute = () => import('@/views/vuerouter/TestRoute.vue');
router.addRoute({
path: '/testroute',
component: TestRoute,
name: 'TestRoute',
});
复制代码
- 移除路由
// 第一种方式:通过 name 删除
router.removeRoute('TestRoute');
复制代码
// 第二种方式:名字重复时自动覆盖
router.addRoute({ path: '/about', name: 'about', component: About })
// 这将会删除之前已经添加的路由,因为他们具有相同的名字且名字必须是唯一的
router.addRoute({ path: '/other', name: 'about', component: Other })
复制代码
- 添加嵌套路由,通过参数1传递⽗路由name即可
router.addRoute('parentRouteName', {...})
复制代码
- 查找已存在路由
router.hasRoute()
:检查路由是否存在。
router.getRoutes()
:获取一个包含所有路由记录的数组。
9. 缓存和过渡动画
缓存
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
复制代码
来回切换页面想要保存组件状态时,可以使用<keep-alive>
结合v-slot
Api包裹动态组件,这时组件实例就会被缓存。
过渡动画
- 基本使用 src/layout/AppMain.vue
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
复制代码
Transition 的所有功能 在这里同样适用,下面定义css动画
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
复制代码
- 单个路由动画
添加
meta
路由元信息,通过meta
设置需要特殊过渡动画
{ path: '', component: VuerouterPage, meta: { transition: 'slide' } },
复制代码
AppMain.vue
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || 'fade'" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
复制代码
/* slide transition 左进右出效果 */
.slide-enter-active,
.slide-leave-active {
transition: 0.5s ease;
}
.slide-enter-from {
transform: translateX(-50px);
opacity: 0;
}
.slide-leave-to {
transform: translateX(50px);
opacity: 0;
}
复制代码
- 根据路由之间关系动态决定动画类型
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition">
<component :is="Component" />
</transition>
</router-view>
复制代码
router.afterEach((to, from) => {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
to.meta.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})
复制代码
本文demo地址 github.com/kongcodes/v…