Vue Router学习笔记(版本:4.2.4)
Vue Router(版本:4.2.4)
一.简介
官方:Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。
vue是单页应用不会有那么多html 让我们跳转 所有要使用路由做页面的跳转
Vue路由允许我们通过不同的 URL 访问不同的内容。通过 Vue 可以实现多视图的单页Web应用
二.快速开始
npm init vue@latest
//或者
npm init vite@latest
...
npm install vue-router@4
Tips:使用 Vue3 安装对应的router4版本;使用 Vue2 安装对应的router3版本
- 在src目录下面新建router文件 然后在router文件夹下面新建index.ts,写入如下内容。同时新建components文件夹,新建两个组件login.vue和reg.vue用于演示跳转
// 1. 定义路由组件.
import { createRouter,createWebHashHistory,RouteRecordRaw } from "vue-router";
// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
const routes: Array<RouteRecordRaw> = [
{
path: "/",
component: ()=>import('../components/login.vue')
},{
path: "/reg",
component: ()=>import('../components/reg.vue')
}
];
// 3. 创建路由实例并传递 `routes` 配置
const router = createRouter({
// 4. 内部提供了 history 模式的实现。
history: createWebHashHistory(),
routes
});
//导出router
export default router;
- 在 main.js 中挂载router组件
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
//引入router
import router from './router'
createApp(App)
//使用router
.use(router)
.mount('#app')
- 在App.vue中写入
<template>
<div>
<h1>哈哈哈</h1>
<hr>
<!--使用 router-link 组件进行导航 -->
<!--通过传递 `to` 来指定链接 -->
<!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
<RouterLink to="/">login</RouterLink>
<hr>
<RouterLink to="/reg">reg</RouterLink>
<hr>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<RouterView></RouterView>
</div>
</template>
<script setup lang="ts"></script>
<style></style>
在页面中就可以看到呈现的效果
三.标签
router-link
请注意,我们没有使用常规的 a 标签,而是使用一个自定义组件 router-link 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。我们将在后面看到如何从这些功能中获益。
router-view
router-view 将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局。
四.基础
前置内容
useRouter()
返回路由器实例:相当于路由的管理者,是一个全局的对象。
useRoute()
返回当前路由对象:一条具体的路由,每一个路由都会有一个route对象。
4.1 路由模式
createWebHashHistory
使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载,其显示的网路路径中会有 “#” 号,有一点点丑。这是最安全的模式,因为他兼容所有的浏览器和服务器。
createWebHistory
美化后的hash模式,会去掉路径中的 “#”。依赖于Html5 的history,pushState API,所以要担心IE9以及一下的版本,感觉不用担心。并且还包括back、forward、go三个方法,对应浏览器的前进,后退,跳转操作。就是浏览器左上角的前进、后退等按钮进行的操作。
createMemoryHistory
适用于所有JavaScript环境,例如服务器端使用Node.js。如果没有浏览器API,路由器将自动被强制进入此模式。
创建一个基于内存的历史。该历史的主要目的是为了处理服务端渲染。它从一个不存在的特殊位置开始。用户可以通过调用 router.push 或 router.replace 将该位置替换成起始位置。
4.1 命名路由
除了path之外,你还可以为任何路由提供name。这有以下优点:
- 没有硬编码的 URL
- params 的自动编码/解码。
- 防止你在 url 中出现打字错误。
- 绕过路径排序(如显示一个)
const routes:Array<RouteRecordRaw> = [
{
path:"/",
name:"Login",
component:()=> import('../components/login.vue')
},
{
path:"/reg",
name:"Reg",
component:()=> import('../components/reg.vue')
}
]
router-link跳转方式需要改变 变为对象并且有对应name
<h1>hahah</h1>
<div>
<router-link :to="{name:'Login'}">Login</router-link>
<router-link :to="{name:'Reg'}">Reg</router-link>
<hr>
<a href="/reg">reg_a</a>
<hr>
<RouterView></RouterView>
</div>
<hr />
同时上面用a标签的形式也可以完成跳转,只不过a标签在跳转的时候会刷新页面,所以基本不用。
Tips:这里
routerlink中的to一定要变成:to对象的形式,不加这个冒号就是字符串的形式
4.2 编程式导航
除了使用 <router-link> 创建 a标签 来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
- 模板
<template>
<div>
<h1>哈哈哈</h1>
<hr>
<RouterLink :to="{name:'logstr'}">logsstr</RouterLink>
<hr>
<RouterLink :to="{name:'regstr'}">regstr</RouterLink>
<hr>
<a href="/">login_a</a>
<hr>
<a href="/reg">reg_a</a>
<hr>
<button @click="toPage('/')">按钮1</button>
<hr>
<button @click="toPage('/reg')">按钮2</button>
<hr>
<RouterView></RouterView>
</div>
</template>
① 字符串模式
import { useRouter } from 'vue-router'
const router = useRouter()
const toPage = () => {
router.push('/reg')
}
② 对象模式
import { useRouter } from 'vue-router'
const router = useRouter()
const toPage = () => {
router.push({
path: '/reg'
})
}
③ 命名式路由模式
import { useRouter } from 'vue-router'
const router = useRouter()
const toPage = () => {
router.push({
name: 'Reg'
})
}
④ a标签跳转
直接通过a href也可以跳转但是会刷新页面
import { useRouter } from 'vue-router'
const router = useRouter()
const toPage = () => {
router.push({
name: 'Reg'
})
}
4.3 历史记录
采用replace进行页面的跳转会同样也会创建渲染新的Vue组件但是在history中其不会重复保存记录,而是替换原有的vue组件
<template>
<div>
<RouterLink replace :to="{name:'logstr'}">logsstr</RouterLink>
<RouterLink replace :to="{name:'regstr'}">regstr</RouterLink>
<button @click="toPage('/')">按钮1</button>
<button @click="toPage('/reg')">按钮2</button>
//横跨历史
<button @click="next">前进</button>
<button @click="prev">后退</button>
<RouterView></RouterView>
</div>
</template>
在编程时导航中使用router.replace实现不记录历史记录
import { useRouter } from 'vue-router'
const router = useRouter()
const toPage = (url: string) => {
router.replace(url)
}
横跨历史:router.go和router.back可以实现在历史堆栈中前进或后退多少步
const next = () => {
//前进 数量不限于1
router.go(1)
}
const prev = () => {
//后退
router.back()
}
4.4 路由传参
① Query
传递
<template>
<div>
<button @click="toPage('/')">按钮1</button>
<hr>
<RouterView></RouterView>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router';
const router = useRouter()
const item = {
name: "啊啊啊",
age: 99,
type: "true"
}
const toPage = (url:string) => {
router.push({
path: url,
query: item
})
}
</script>
接收
<template>
<div>
<h1>login</h1>
<div>
name: {{ route.query.name }}
age: {{ route.query.age }}
type: {{ route.query.type }}
</div>
</div>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router';
const route = useRoute()
</script>
这种传参方式类似GET请求类型,参数会写在地址栏上
Tips:编程式导航使用
router push或者replace的时query必须传入一个对象,它只接受对象。❓❓❓:网上资料说使用
query方式进行传参必须使用路由的path不能使用name,但是我试了一下好像都行,改版了?
② Params❗️(弃用)
4.1.4 (2022-08-22)版本后,方法会失效
const toDetail = (item: Item) => {
router.push({
name: 'Reg',
params: item
})
}
接受参数使用 useRoute 的 params
<template>
<div>品牌:{{ route.params.name }}</div>
<div>ID:{{ route.params.id }}</div>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router';
const route = useRoute()
</script>
这种传参方式类似POST请求类型,参数会写在地址栏上
Tips:由于
params方式进行传参,必须使用路由的name,并且这种方式传递的参数,内容是在内存中,页面刷新数据会丢失。
③ 动态路由传参
在创建路由的时候在路径上拼接参数
...
{
path: "/:id/:name",
name: "logstr",
component: ()=>import('../components/login.vue')
},{
path: "/reg/:id/:name",
name: "regstr",
component: ()=>import('../components/reg.vue')
}
...
然后再调用路由的时候,使用params方式进项调用
const toDetail = (item: Item) => {
router.push({
name: 'Reg',
params:{
id: item.ids,
name: item.name
}
})
}
Tips
- 注意此处一定用
name匹配路由,否则无效- 同时
params中的参数的Key要和路由中的Key一致才可以- 路由中设置的参数都要赋值,否则控制台报错
接受参数使用 useRoute 的 params
<template>
<div>品牌:{{ route.params.name }}</div>
<div>ID:{{ route.params.id }}</div>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router';
const route = useRoute()
</script>
这种情况下,参数还是会展示在地址栏中。
4.5 嵌套路由
在父路由下面加children
{
path: "/father",
name: "fstr",
component: () => import('../components/father.vue'),
children: [
{
//子路由前不需要加斜杠
path: "children1",
name: "cstr1",
component: () => import('../components/children1.vue')
},{
path: "children2",
name: "cstr2",
component: () => import('../components/children2.vue')
}
]
}
Tips:子路由的路径前面不需要加斜杠
4.6 命名视图
在路由的基础上使用命名视图,也就是通过RouterView标签中的name来匹配路由配置文件中的components中配置的不同组件,其中default是默认的视图,不需要name进行匹配。
模板:
<template>
<div>
<h1>
父路由!!
</h1>
<RouterLink :to="{name:'cstr1'}">cstr1</RouterLink>
<hr>
<RouterLink :to="{name:'cstr2'}">cstr2</RouterLink>
<hr>
<RouterView></RouterView>
<RouterView name="aaa"></RouterView>
<RouterView name="bbb"></RouterView>
</div>
</template>
路由:
{
path: "/father",
name: "fstr",
component: () => import('../components/father.vue'),
children: [
{
//子路由前不需要加斜杠
path: "children1",
name: "cstr1",
components: {
default: () => import('../components/children1.vue')
}
},{
path: "children2",
name: "cstr2",
components: {
aaa: () => import('../components/children1.vue'),
bbb: () => import('../components/children2.vue')
}
}
]
}
Tips:是
components不是component,否则不生效
4.7 路由重定向-别名
① 重定向
重写目前的浏览器路径至指定的地址,重定向也是通过 routes 配置来完成
{
path: "/father",
name: "fstr",
//重定向到一个命名的路由
//redirect: {
// name: 'cstr2'
//} ,
//重定向到一个绝对地址
//redirect: "/father/children2",
//重定向到一个方法,动态返回重定向目标:
redirect: to => {
//方法接收目标路由作为参数
//return 重定向的字符串路径/路径对象
return {
path: '/father/children1',
query:
{
name:"123",
age: "456"
}
}
},
component: () => import('../components/father.vue'),
children: [
{
//子路由前不需要加斜杠
path: "children1",
name: "cstr1",
components: {
default: () => import('../components/children1.vue')
}
},{
path: "children2",
name: "cstr2",
components: {
aaa: () => import('../components/children1.vue'),
bbb: () => import('../components/children2.vue')
}
}
]
}
也可以重定向到相对位置:
官方代码:相对重定向
const routes = [
{
// 将总是把/users/123/posts重定向到/users/123/profile。
path: '/users/:id/posts',
redirect: to => {
// 该函数接收目标路由作为参数
// 相对位置不以`/`开头
// 或 { path: 'profile'}
return 'profile'
},
},
]
❓❓❓:官网重定向到相对路径的写法我本地有问题,我本地版本是4.2.4,我按照官方的写的时候会定位到绝对地址而非相对。
② 别名
alias 就是给地址换个名,通过别名,你可以自由地将 UI 结构映射到一个任意的 URL,而不受配置的嵌套结构的限制。使别名以 / 开头,以使嵌套路径中的路径成为绝对路径。你甚至可以将两者结合起来
{
path: "/father",
name: "fstr",
alias: ['/f', '/f2'],
component: () => import('../components/father.vue'),
children: [
{
//子路由前不需要加斜杠
path: "children1",
name: "cstr1",
// 为这 3 个 URL 呈现 children1 ,也可以吧。father 换成/f
// - /father/children1
// - /father/c2
// - /c1
alias: ['/c1', 'c2'],
components: {
default: () => import('../components/children1.vue')
}
}, {
path: "children2",
name: "cstr2",
components: {
aaa: () => import('../components/children1.vue'),
bbb: () => import('../components/children2.vue')
}
}
]
}
Tips:如果你的路由有参数,请确保在任何绝对别名中包含它们:
4.8 滚动行为
当在页面切换了在切换回来的时候,制滚还在你上次的位置,如果没有滚动条,就会在顶上。可以返回一个 Promise 来延迟滚动。
savedPosition就是他自己记录的之前页面你在哪里,直接返回就行,或者自己定义top都可以。
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
});
五.进阶
5.1 导航守卫
5.1.1全局前置守卫
使用 router.beforeEach 注册一个全局前置守卫:
const router = createRouter({ ... })
router.beforeEach((to, from) => {
// ...
// 返回 false 以取消导航
return false
})
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。
每个守卫方法接收两个参数:
to: 即将要进入的目标 (到哪儿去)from: 当前导航正要离开的路由 (从哪儿来)
可以返回的值如下:
false: 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from路由对应的地址。- 一个路由地址: 通过一个路由地址跳转到一个不同的地址,就像你调用
router.push()一样,你可以设置诸如replace: true或name: 'home'之类的配置。当前的导航被中断,然后进行一个新的导航,就和from一样。
router.beforeEach(async (to, from) => {
if (
// 检查用户是否已登录
!isAuthenticated &&
// ❗️ 避免无限重定向
to.name !== 'Login'
) {
// 将用户重定向到登录页面
return { name: 'Login' }
}
})
如果遇到了意料之外的情况,可能会抛出一个 Error。这会取消导航并且调用 router.onError() 注册过的回调。
如果什么都没有,undefined 或返回 true,则导航是有效的,并调用下一个导航守卫
Tips:在之前的 Vue Router 版本中,也是可以使用 第三个参数
next的。这是一个常见的错误来源,可以通过 RFC 来消除错误。然而,它仍然是被支持的,这意味着你可以向任何导航守卫传递第三个参数。在这种情况下,确保next在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。
//白名单
const whiteList = ['/login', '/register'];
router.beforeEach((to,from,next)=>{
if (whiteList.includes(to.path)) {
next()
} else {
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
}
})
5.1.2全局解析守卫
你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,因为它在每次导航时都会触发,不同的是,解析守卫刚好会在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。这里有一个例子,确保用户可以访问自定义 meta 属性 requiresCamera 的路由:
router.beforeResolve 是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// ... 处理错误,然后取消导航
return false
} else {
// 意料之外的错误,取消导航并把错误传给全局处理器
throw error
}
}
}
})
5.1.3全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
它们也反映了 navigation failures 作为第三个参数:
router.afterEach((to, from) => {
sendToAnalytics(to.fullPath)
})
//也可以用 navigation failures 作为第三个参数:
router.afterEach((to, from, failure) => {
if (!failure) sendToAnalytics(to.fullPath)
})
5.2 路由元信息
通过路由记录的 meta 属性可以定义路由的元信息。使用路由元信息可以在路由中附加自定义的数据,例如:
- 权限校验标识。
- 路由组件的过渡名称。
- 路由组件持久化缓存 (keep-alive) 的相关配置。
- 标题名称
我们可以在导航守卫或者是路由对象中访问路由的元信息数据。
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
component: () => import('@/views/Login.vue'),
meta: {
title: "登录"
}
}
]
})
5.3 动态路由
我们一般使用动态路由都是后台会返回一个路由表,前端通过调接口拿到后处理(后端处理路由)
动态路由主要通过两个函数实现。router.addRoute() 和 router.removeRoute()。它们只注册一个新的路由,也就是说,如果新增加的路由与当前位置相匹配,就需要你用 router.push() 或 router.replace() 来手动导航,才能显示该新路由
router.addRoute({
path: '/children3',
name: 'cstr3',
component: ()=>(import('../components/children3.vue'))
})
如果你决定在导航守卫内部添加或删除路由,你不应该调用 router.replace(),而是通过返回新的位置来触发重定向:
router.beforeEach(to => {
if (!hasNecessaryRoute(to)) {
router.addRoute(generateRoute(to))
// 触发重定向
return to.fullPath
}
})
上面的例子有两个假设:第一,新添加的路由记录将与 to 位置相匹配,实际上导致与我们试图访问的位置不同。第二,hasNecessaryRoute() 在添加新的路由后返回 false,以避免无限重定向。
因为是在重定向中,所以我们是在替换将要跳转的导航,实际上行为就像之前的例子一样。而在实际场景中,添加路由的行为更有可能发生在导航守卫之外,例如,当一个视图组件挂载时,它会注册新的路由。

浙公网安备 33010602011771号