前言
由于Vue框架是SPA(单页面应用)不会有不同的html提供给我们跳转,所以要使用路由进行页面的跳转,Vue 路由允许我们通过不同的 URL 访问不同的内容。通过 Vue 可以实现多视图的单页Web应用,由于Vue3是未来主流,本文以Vue3的路由配置为主
安装
Vue2安装Vue-router3.0的版本
npm install vue-router@3
Vue3安装Vue-router4.0的版本
npm install vue-router@4
路由模式
1、哈希模式(url携带#)
本质是通过location.hash = '/目标url'来实现跳转
window.addEventListener('hashchange',(event)=>{
console.log(event,'路由相关信息')
})
2、history模式(url不携带#)
本质通过history.pushState(传递参数,标题,URL)
window.addEventListener('popstate',(event)=>{
console.log(event,'路由相关信息')
})
在Vue3中的使用
//引入路由对象
import { createRouter, createWebHistory, createWebHashHistory, createMemoryHistory, RouteRecordRaw } from 'vue-router'
//路由数组的类型 RouteRecordRaw
const routes: Array<RouteRecordRaw> = [{
path: '/login',
name:'login',
alias:['别名1','别名2'],
redirect:'register'
component: () => import('../components/login.vue'),
meta:{},
children:[]
},{
path: '/register',
name: '/register',
component: () => import('../components/register.vue')
}]
const router = createRouter({
history: createWebHashHistory(),
routes: routes,
scrollBehavior() {
return {
el: '#app',
top: 0,
behavior: 'smooth',
};
},
});
//导出router
export default router
路由的跳转方式
1、命名路由:即通过path和name进行跳转
<router-link :to="{name:name}">Login</router-link>
<router-link :to="path">Login</router-link>
2、编程式导航:即通过Vue-router提供的函数方法跳转
import { useRouter } from 'vue-router'
const router = useRouter(
const toPage = () => {
// router.push 产生历史记录
router.push({
path: '目标地址'|| name: '目标name'
})
// router.replace 不会产生历史记录 直接替代当前页面
router.replace({
path: '目标地址'|| name: '目标name'
})
// 从历史记录从前进后退 数字可正可负
router.go(数字)
// 后退
router.back()
}
3、a标签跳转:跳转但是会刷新页面
<a href="/目标地址">提示文字</a>
路由的传参 path搭配query name搭配params
1、query传参
import { useRouter,useRoute } from 'vue-router'
// 传递参数
const router = useRouter(
const toPage = () => {
router.push({
path: '目标url',
query:'参数对象'
})
}
// 接收参数
const route = useRoute()
console.log(route.'参数对象')
2、params传参(存在页面刷新参数丢失的问题)
import { useRouter,useRoute } from 'vue-router'
// 传递参数
const router = useRouter(
const toPage = () => {
router.push({
name: '目标name',
params:'参数对象'
})
}
// 接收参数
const route = useRoute()
console.log(route.'参数对象')
命名视图(有点类似Vue的具名插槽)
控制同个页面下的不同组件位置(其实动态组件引入也一样)
<template>
<router-view name='K'></router-view>
<router-view name='Q'></router-view>
</template>
const routes: Array<RouteRecordRaw> = [{
path: '/K',
name:'K',
component:{
K => import('../components/K.vue'),
Q => import('../components/Q.vue')
}
meta:{},
children:[]
}]
路由守卫
1、全局前置路由
router.beforeEach(async (to, from, next) => {
// to: Route 目标路由;
// from: Route 当前路由;
// next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
// next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
// next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
});
案例
const whileList = ['/'] // 路由白名单
router.beforeEach((to, from, next) => {
NProgress.start();
let token = localStorage.getItem('token')
// 如果目标路由在白名单内 或者本地有存储token则放行路由
if (whileList.includes(to.path) || token) {
next()
NProgress.done();
} else {
next({
path:'/'
})
NProgress.done();
}
})
2、全局后置路由
router.afterEach((to,from) => {
// 一般用于网页加载条的渲染
NProgress.done();
});
案例
<template>
<div class="wraps">
<div ref="bar" class="bar"></div>
</div>
</template>
<script setup lang='ts'>
import { ref, onMounted } from 'vue'
let speed = ref<number>(1)
let bar = ref<HTMLElement>()
let timer = ref<number>(0)
const startLoading = () => {
let dom = bar.value as HTMLElement;
speed.value = 1
timer.value = window.requestAnimationFrame(function fn() {
if (speed.value < 90) {
speed.value += 1;
dom.style.width = speed.value + '%'
timer.value = window.requestAnimationFrame(fn)
} else {
speed.value = 1;
window.cancelAnimationFrame(timer.value)
}
})
}
const endLoading = () => {
let dom = bar.value as HTMLElement;
setTimeout(() => {
window.requestAnimationFrame(() => {
speed.value = 100;
dom.style.width = speed.value + '%'
})
}, 500)
}
defineExpose({
startLoading,
endLoading
})
</script>
import loadingBar from './components/loadingBar.vue'
// 将组件转为虚拟dom
const Vnode = createVNode(loadingBar)
// 使用render函数渲染到真正的dom
render(Vnode, document.body)
router.beforeEach((to, from, next) => {
// 组件中使用defineExpose暴露方法 则用以下方式调用
Vnode.component?.exposed?.startLoading()
})
router.afterEach((to, from) => {
Vnode.component?.exposed?.endLoading()
})
路由元信息
import {RouteRecordRaw} 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
// 自定义扩展
isAlive?: boolean
hidden: boolean
transition:string
}
}
const routes: Array<RouteRecordRaw> = [{
path: '/login',
name:'login',
alias:['别名1','别名2'],
redirect:'register'
component: () => import('../components/login.vue'),
meta:{isAlive:true,hidden:false,transition:'animate_fadeIn'},
children:[]
},
路由过渡动画效果(搭配animation.css使用)
<template>
<router-view #default="{route,Component}">
<transition :enter-active-class="`animate_animated ${route.meta.transition}`">
<keep-alive :include="aliveViews">
<component :is="Component"/>
</keep-alive>
</transition>
</router-view>
</template>
<script setup lang="ts">
import 'animate.css'
</script>
路由滚动行为
创建路由时配置scrollBehavior方法
const router = createRouter({
history: createWebHashHistory(),
routes: allRoutes,
scrollBehavior(to,from,savePosition) {
// savePosition 自动记录当前页面滚动到的位置
return {
el: '#app',
top: 0,
behavior: 'smooth',
};
},
});
动态路由
根据不同用户权限后端返回不同的路由表进行渲染
1、添加路由 router.addRoute(routeObj)
router.addRoute({ path: '/about', name: 'K', component: About })
2、删除路由 router.removeRoute(routeName)
在路由中name使用 Symbol
类型限制防止冲突
router.removeRoute('K') // 当路由被删除时,所有的别名和子路由也会被同时删除
3、判断路由是否存在 router.hasRoute(routeName)
hasRoute(name: string | symbol): boolean