项目的目录结构:
1. Vue-Router的使用
安装Vue-route
pnpm add vue-router@4
创建router文件
- /route/index.vue
import { createRouter } from "vue-router";
import {
createMemoryHistory,
createWebHashHistory,
createWebHistory,
} from "vue-router";
import type { RouteRecordRaw } from "vue-router";
// 2. 定义路由
const routes: RouteRecordRaw[] = [
{
path: "/", // 浏览器中的路径
name: "login", // 路由名称(非必需)
component: () => import("@/views/login.vue"), // 路由组件
},
{
path: "/home",
name: "home",
component: () => import("@/views/home.vue"),
},
{
path: "/about",
component: () => import("@/views/about.vue"),
},
];
// 1. 创建路由器实例
const router = createRouter({
// 缺点:浏览器没有前进后退按钮
history: createMemoryHistory(),
// 缺点:地址中有#号不美观;优点:兼容性是最好的,不需要服务器做任何配置
// history: createWebHashHistory(),
// 缺点:需要服务器做适配;优点:支持浏览器的前进/后退;
// history: createWebHistory(),
routes,
});
export default router;
- 关于历史记录模式
- 历史记录模式共有三种,各有优缺点
- 官网推荐使用
createWebHistory
,后端服务器的配置也不麻烦 - 不注重美观的后台管理网站中推荐使用
createWebHashHistory
router
和route
两个的关系router
:路由器,是路由的管理者,管理着所有的路由route
:路由,一条一条的线路,浏览器url对应着vue
在main.ts中全局引用该插件
// 引入插件vue-router
import router from './router'
createApp(App).use(router).mount('#app')
如何使用
在template中通过router-link
导航
App.vue
代码
<template>
<div>
<!-- 通过路由的name 属性,跳转到对应的路由地址 -->
<router-link :to="{ name: 'home' }">主页</router-link>
<!-- 通过路由的path 属性,跳转到对应的路由地址 -->
<router-link to="/about">关于</router-link>
<router-link to="/">登录</router-link>
</div>
<hr />
<router-view></router-view>
</template>
<script setup lang="ts"></script>
router-link
:可理解为特殊的a标签,不同的是router-link
不会在跳转时引起页面的抖动router-view
:可理解为占位符,会在它所在的位置渲染当前 URL 路径对应的路由组件
在script中通过router实例导航(编程式导航)
- 在实际应用中,通常在渲染某个页面时需要执行一些操作,要使用编程式导航
父组件App.vue
<template>
<div>
编程式导航:
<button @click="toLogin">登录</button>
<button @click="toHome('/home')">主页</button>
<button @click="toAbout('/about')">关于</button>
<button @click="toLogin1('login')">登录</button>
</div>
<hr />
<router-view></router-view>
</template>
<script setup lang="ts">
// 获取路由器实例
const router = useRouter();
console.log(router);
// 返回当前路由地址
const route = useRoute();
console.log(route);
// 字符串形式跳转
const toLogin = () => {
router.push("/");
};
// 字符串传参
const toHome = (url: string) => {
router.push(url);
};
// 对象的形式,传入path
const toAbout = (url: string) => {
router.push({
path: url,
});
};
// 对象形式,传入name
const toLogin1 = (name: string) => {
router.push({
name,
});
};
</script>
Router
实例- 获取
Router
实例:const router = useRouter();
- 路由器实例中有什么:
- 获取
Route
实例- 获取
Route
实例:const route = useRoute();
Route
实例中有什么:
- 获取
replace 不保存历史记录
- 在
<router-link>
加入属性replace
<router-link replace to="/">登录</router-link>
- 编程式导航,跳转使用
router.repalce
// 字符串形式跳转
const toLogin = () => {
router.replace("/");
};
- 前进、后退
<button @click="next">前进</button>
<button @click="prev">后退</button>
// 页面前进
const next = () => {
// go中的数字代表前进几个页面
router.go(1)
}
// 页面后退
const prev = () => {
// router.go(-1)
router.back()
}
- 在
router
实例中可以看到back
方法其实调用的就是go(-1)
方法
2. 路由传递数据
2.1 query传参
-
和向服务器发送get请求一样,通过浏览器传递参数
-
发送方代码:
import { useRouter} from "vue-router";
const data = ref({
name:"Tom",
age:18
})
const router = useRouter();
// 通过query传参
const sendData = () => {
router.push({
// path:"/about",
name:"about",
query:data.value
})
}
- 接收方代码:
<template>
<div>
姓名:{{ route.query.name }} <br>
年龄:{{ route.query.age }}
</div>
</template>
<script setup lang="ts">
import { useRoute } from "vue-router";
const route = useRoute();
- 浏览器中显示
http://localhost:5173/about?name=Tom&age=18
3. 嵌套路由children
- 重点是
children[]
const routes1: RouteRecordRaw[] = [
{
path: "/user", // 浏览器中的路径
name: "f-route", // 路由名称(非必需)
component: () => import("@/router-child/fRoute.vue"), // 路由组件
children: [
{
path: "", // 默认加载子路由
name: "sonRoute1",
component: () => import("@/router-child/sonRoute1.vue"),
},
{
path: "sonRoute2",
name: "sonRoute2",
component: () => import("@/router-child/sonRoute2.vue"),
},
],
},
];
使用子路由
path
以/
开头的是绝对路径,它是完整路径不受父路径影响,没有/
是相对路径,需要向上找父级路径,直到找到带/
的路径,并且把路径都拼接起来
<router-link>
使用的是绝对路径:父路由的path
+/
+子路由的path
<router-link to="/user">第一个子路由</router-link>
<router-link :to="{name:'sonRoute2'}">第二个子路由</router-link>
<router-view></router-view>
4. 命名路由components
- 在routes中定义组件的名字
- 同一视图中展示多个组件,使用
components
的对象形式 - 给组件起个名字(名字随意,没有限制)
- 同一视图中展示多个组件,使用
const routes2: RouteRecordRaw[] = [
{
path: "/", // 根路径
component: () => import("@/name-views/root.vue"), // 路由组件
children: [
{
path: "showLeft",
name: "showLeft",
components:{
default:() => import("@/name-views/a.vue"), // default对应的是<router-view></router-view>
bbb:() => import("@/name-views/b.vue"), // bbb对应的是 <router-view name="bbb">
}
},
{
path: "showRight",
name: "showRight",
components: {
default:() => import("@/name-views/a.vue"),
ccc:() => import("@/name-views/c.vue"), // ccc对应的是 <router-view name="ccc">
}
},
],
},
];
- 在vue中使用命名组件
components
中的default
对应到vue中的代码是<router-view></router-view>
components
中包含几个组件,浏览器中显示几个组件的视图- 组件在浏览器的位置是由
<router-view>
所在的位置决定的
<router-link to="/showLeft">显示左侧导航</router-link><br />
<router-link :to="{name:'showRight'}">显示右侧导航</router-link>
<div>
<router-view name="bbb"></router-view>
<router-view></router-view>
<router-view name="ccc"></router-view>
</div>
- 浏览器显示结果
5. 重定向redirect
访问某一个路由时,自动跳转到重定向的路由
- 字符串形式
const routes: RouteRecordRaw[] =[
{
path:"/",
redirect::"/home", // 使用绝对路径
}
]
- 对象形式
const routes: RouteRecordRaw[] =[
{
path:"/",
redirect:{
// path:"/home"
name:"home"
},
}
]
- 方法
const routes = [
{
path: '/',
redirect: to => {
console.log(to) // to:当前路径的相关信息
return {
path: '/home', // 重定向的绝对路径
query: { name:"小满" } // 通过路由传参,看上面的query传参
}
},
},
]
6. 别名alias
就是给路径起个别的名字,必须是
绝对路径
- 字符串
const routes: RouteRecordRaw[] =[
{
path:"/",
alias:"/xiaoman",
}
]
- 数组方式
const routes: RouteRecordRaw[] =[
{
path:"/",
alias:["/xiaoman","/daman"],
}
]
- 使用
http://localhost:5173/daman
http://localhost:5173/xiaoman
上面的两个地址都是指向
http://localhost:5173/
7. 路由守卫
- 在路由发生跳转前,和跳转发生后执行一些代码
- 特别注意:在守卫中跳转后,也是发生了路径的变化,所以还会重新走一次路由守卫,小心死循环
前置守卫
- 当浏览器中地址发生改变时触发
import router from "./index" // 记得导入已经创建的路由器router
router.beforeEach((to, from) => {
console.log("to:" ,to, "from:",from)
})
- 在守卫中的路由跳转
router.beforeEach((to, from) => {
// 各种条件判断
if(to.path !== '/' && !localStorage.getItem('token')){
return false // 取消当前的导航,不会向任何路由跳转
return "/" // return的值会赋值到 to.path
return { path: '/' } // return一个对象
return {name:'login'}
}
})
-
to
将要进入的目标路由
-
from
将要离开的路由
-
第三个参数next(官网不建议使用)
next()的作用:next()就是通过的意思,但是通过后会继续走剩下的代码,并且next()在代码中会调用多次,不容易察觉错误
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
后置守卫
- 路由加载后执行,执行的时间在vue组件加载之前
router.afterEach((to, from) => {
// todo
})
实现页面加载进度条nprogress
小满视频里面是手写的,我看的困,使用现成的
首先这个进度条是假的,并不能真正的表示加载页面的进度
- 安装
pnpm add nprogress
- 在路由中使用
import NProgress from "nprogress"
import 'nprogress/nprogress.css' //这个样式必须引入
NProgress.configure({ showSpinner: false }); // 取消右侧的转圈圈
// 页面跳转时开始
router.beforeEach((to, from) => {
NProgress.start() //
})
// 路由成功跳转完成后
router.afterEach((to, from) => {
NProgress.done()
})
- 效果
8. 路由元信息
路由中添加meta
属性
- 在每条单独的路由上可以添加
meta
属性,meta
属性是一个对象,对象中可以添加很多信息
{
path:"/",
component: () => import("@/views/index.vue"),
meta:{
title:'标题栏显示',
show: true,
}
}
在.d.ts
中增加meta
的类型
import "vue-router"
declare module 'vue-router'{
interface RouteMeta{
title:string
icon?:string
}
}
在守卫中获取meta
router.beforeEach((to, from) => {
// to.meta
document.title = to.meta.title
})
在vue
中获取meta
import {useRoute} from 'vue-router'
const route = useRoute()
console.log(route.meta.title)
获取父级路由的meta
假设当前路由有父级路由,则通过数组
matched[]
访问
import {useRoute} from 'vue-router'
const route = useRoute()
console.log(route.meta.title) // 当前路由的meta
console.log(route.matched[0].meta.title) // 父级路由的meta
9. 路由应用示例
1.过渡动效
- 给
<router-view>
加动画效果
使用animatecss的动画效果,参考:animatecss动画效果
- 安装animatecss
pnpm add animate.css
- 在元数据中加入要采用的动画效果
{
path: "/",
name: "login",
component: () => import("@/views/login.vue"),
meta: {
title: "登录",
transition:"animate__fadeInDown" // 动画效果来自animatecss
},
记得修改.d.ts中RouteMeta的类型
- 在要使用动效的vue中引入
import "animate.css";
<template>
<div>
<router-view #default="{route,Component}">
<!-- 在路由的meta中配置的动画效果,放在这里使用 -->
<Transition :enter-active-class="`animate__animated ${route.meta.transition}`">
<component :is="Component"></component>
</Transition>
</router-view>
</div>
</template>
- 插槽中的
route
和Component
是固定的,这段代码中唯一能修改的就是Transition标签
的属性 route
:当前路由,通过route.meta
访问元数据中的值Component
:vNode,路由即将加载的vue组件
2. 滚动行为
- 记录某个组件的滚动距离,跳转到别的路由再返回时,自动滚动到刚才的位置
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) { // savedPosition记录滚动的距离
// return 期望滚动到哪个的位置
// 有滚动就滚动到记录的位置,没有滚动就回到初始位置
return savedPosition ? savedPosition : { left: 0, top: 0 };
},
});
-
为什么不能直接
return savedPosition
,如果vue中没有设置滚动条,savedPosition
返回值是null
,会报错 -
style="overflow: auto;"
想要记录滚动位置,必须在当前的vue中添加该样式假设有父级的
vue
,两个子级的vue
,想要在两个子级的vue
中记录滚动行为,那么两个子级的vue
必须都加上style="overflow: auto;"
;如果只在父级中加该样式,滚动是可以实现,但是savedPosition
的值为null
-
延时滚动
scrollBehavior(to, from, savedPosition) {
// return 期望滚动到哪个的位置
return new Promise((resolve)=>{
setTimeout(() => {
resolve(savedPosition ? savedPosition : { left: 0, top: 0 });
},1000)
})
// return savedPosition ? savedPosition : { left: 0, top: 0 };
},