router路由
router-view
<router-link :to="{ name: 'Login' }">Login</router-link>
router-link
函数式导航
第一种(字符串式)
<button @click="toPage('/')">Login</button>
import { useRouter } from "vue-router";
const router = useRouter();
const toPage = (url: string) => {
router.push(url);
};
第二种(对象式)
<button @click="toPage('/')">Login</button>
import { useRouter } from "vue-router";
const router = useRouter();
const toPage = (url: string) => {
router.push({
path:url
})
};
第三种(命名式)
<button @click="toPage('Login')">Login</button>
import { useRouter } from "vue-router";
const router = useRouter();
const toPage = (url: string) => {
router.push({
path:url
})
};
router历史记录
第一种:router-link
//加上replace标签,取消历史记录
<router-link replace :to="{ name: 'Login' }">Login</router-link>
第二种:编程式
<button @click="toPage('/')">Login</button>
const toPage = (url: string) => {
router.replace({
path:url
})
};
第二种:函数式
<button @click="next">next</button>
<button @click="prev">prev</button>
//num == 几次历史记录
const next = () => {
router.go(${num})
}
const prev = () => {
router.back(${num})
}
路由传参
第一种 地址栏传参 :query
<button @click="toDetail(item)">详情</button>
type Item = {
定义好类型
// name: string;
// price: number;
// id: number;
};
const toDetail = (item: Item) => {
router.push({
path: "/reg",//路由地址
query:item//传值
});
};
//reg 接收
/html
{{route.query.item}}
/html
/script
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
/script
第二种 动态路由
const toDetail = (item: Item) => {
router.push({
name: "Reg",// 必须使用 路由约定的name
params:{
id:item.id
}
});
};
'/reg'
//查找到这一项
const item = data.find((item) => item.id === Number(route.params.id));
/html
{{item?.name }}
{{ item?.price }}
{{ item?.id }}
/html
嵌套路由
{
path:"/user",
component:() => import('../components/footer.vue'),
children:[
{
path:"",
name:'Login',
component:() => import('../components/login.vue')
},{
path:"reg",
name:'Reg',
component:() => import('../components/reg.vue')
}
]
},
命名视图
{
path:'/',
component:()=> import ('../components/root.vue'),
children:[
{
path:'/user1',
components:{ //一定是加s
default:()=>import ('../components/A.vue')
}
},
{
path:'/user2',
components:{ //一定是加s
bbb:()=>import ('../components/B.vue'),
ccc:()=>import ('../components/C.vue')
}
}
]
},
/html
<div>
<router-link to="/user1">/user1</router-link>
<router-link to="/user2">/user2</router-link>
<hr />
<router-view></router-view>
<router-view name="bbb"></router-view>
<router-view name="ccc"> </router-view>
</div>
/html
重定向
(三种方式)
alias:['/root','/root1','/root2'],
redirect:"/user1",
redirect:{
path:"/user1"
},
redirect:to=>{
return '/user1'
},
return{//传参数
path:'user1',
query:{
name:"ckj",
age:20
}
}
导航守卫
前置
const whileList = ['/'] //白名单
router.beforeEach((to,from,next)=>{
//要么白名单之内,要么已经登陆过 才放行 next()
if(whileList.includes(to.path)||localStorage.getItem('token')){
next()
}else{
next('/')
}
})
后置(制作进度条)
<template>
<div class="wrapper">
<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 + "%";
});
}, 1000);
};
//暴露给全局方法
defineExpose({
startLoading,
endLoading,
});
</script>
<style lang="less" scoped>
.wrapper {
position: fixed;
top: 0;
width: 100%;
height: 2px;
.bar {
height: inherit;
width: 0;
background-color: rgb(43, 200, 19);
}
}
</style>
import loadingBar from '@/components/loadingBar.vue'
import { createVNode,render } from 'vue'
//使用Vnode转换一真实dom
const Vnode = createVNode(loadingBar)
render(Vnode,document.body)//挂载在全局上
router.beforeEach((to,from,next)=>{
Vnode.component?.exposed?.startLoading()
})
router.afterEach((to,from)=>{
Vnode.component?.exposed?.endLoading()
})
路由元组件
title
/路由.ts
declare module 'vue-router'{
interface RouteMeta{
title:string,
}
}
{
path:'/',
component:()=>import('@/views/Login.vue'),
meta:{
title:'登录页面',
}
},
/路由.ts
/main.ts
router.beforeEach((to,from,next)=>{
console.log(to);
document.title = to.meta.title
})
/main.ts
transition
/路由.ts
declare module 'vue-router'{
interface RouteMeta{
title:string,
transition:string
}
}
{
path:'/',
component:()=>import('@/views/Login.vue'),
meta:{
title:'登录页面',
transition:"animate__fadeIn"
}
},
/路由.ts
/app.vue
<template>
<div>
<router-view #default="{route,Component}">
<transition :enter-active-class="`animate__animated ${route.meta.transition}`">
<component :is="Component"></component>
</transition>
</router-view>
</div>
</template>
<script setup lang='ts'>
//npm i animate.css
import 'animate.css'
</script>
/app.vue
滚动行为
const router = createRouter({
history:createWebHistory(),
//保存跳转前的距离
scrollBehavior:(to,from,savePosition)=>{
if(savePosition){
return savePosition
}else{
return{
top:0
}
}
},
routes:[
]
})
动态路由
转过去路由 刷新才正常显示
debug:
- 检查路由信息是否有误、是否多空格.vue插件观察routes是否添加正确
- 放行 next(),其余next(‘/login’)、next(to)等等都不是放行,是关闭一个新打开一个
- 一定不要忘记抱起来,同时增加一个父级div包裹以防万一