共计 9225 个字符,预计需要花费 24 分钟才能阅读完成。
1. Vue router 4 基础
在构建现代
Web
应用时,单页应用(SPA
)因其流畅的用户体验和快速的页面切换能力,成为了众多项目的首选架构。
然而,在SPA
中,随着应用功能的日益复杂,权限控制成为了一个不可忽视的问题。如何确保不同用户只能访问其被授权的资源,是保障应用安全和数据一致性的关键。
在这个过程中,路由管理扮演着至关重要的角色。Vue Router
,作为Vue
官方提供的路由管理器,不仅能够帮助我们定义应用的页面结构和导航逻辑,还能与权限控制机制紧密结合,实现细粒度的访问控制。
在Vue3
项目中,通过Vue Router
的路由守卫功能,我们可以在用户访问某个路由之前进行权限验证。例如,我们可以利用全局前置守卫(beforeEach
)来检查用户的登录状态和权限级别,从而决定是否允许用户继续访问目标页面。如果用户未登录或权限不足,我们可以重定向用户到登录页面或提示页面,确保应用的安全性。
此外,Vue Router
还支持路由元信息(meta
字段),这为我们在路由层面定义额外的信息提供了便利。通过路由元信息,我们可以为不同的路由设置不同的权限要求,并在路由守卫中根据这些信息进行权限验证。这种方式使得权限控制更加灵活和可配置。
1.1 安装
可以使用 npm
包管理器直接安装,或者在创建 Vue3
新项目时,使用 create-vue
创建一个基于 Vite
的项目,并选择加入 vue-router
选项。
npm install vue-router@4
or
npm create vue@latest
1.2 配置
安装完成后,接下来需要在项目中配置Vue Router
。这通常涉及以下几个步骤:
- 创建路由实例:首先,需要导入
Vue Router
并创建一个路由实例。在这个过程中,需要定义应用的路由配置,包括各个路由的路径(path
)、组件(component
)等信息。 - 挂载路由实例:然后,需要将创建的路由实例挂载到
Vue
应用中。这通常是在创建Vue
应用实例时,通过createApp
函数的.use()
方法完成的。
Vue Router 4
进行路由管理时,使用 createRouter()
函数来创建并配置路由器实例。createRouter()
函数替代了 Vue Router 3
中的 new VueRouter()
函数。
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'
const routes = [
{ path: '/', component: HomeView },
{ path: '/about', component: AboutView },
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
template>
RouterView>/RouterView>
/template>
script setup>
import { RouterView } from 'vue-router'
/script>
1.3 基本属性
path
:路由路径,字符串。应该以/
开头,除非该路由为另一条路由的子路由。当浏览器的URL
与这个路径匹配时,就会渲染对应的组件。name
:路由名称,字符串,必须唯一。命名路由可以在编程式导航中通过名称来引用路由,而不是通过路径字符串。component
:路由组件(通常是导入的组件)。这个组件会在路由匹配时渲染。children
:嵌套路由。redirect
:路由重定向。beforeEnter
:路由导航守卫。props
:允许将参数作为props
传递给由router-view
渲染的组件。meta
:路由元信息,一个包含自定义信息的对象,用于存储路由的额外信息,如路由标题、是否需要登录等。这个对象可以在路由守卫(如在导航守卫中使用to.meta.requiresAuth
)中被访问,用于控制路由的访问权限、添加页面标题等。
meta.title
:路由标题。meta.requiresAuth
:是否需要登录。meta.keepAlive
:是否缓存路由组件。meta.icon
:路由图标。meta.hidden
:是否在菜单中隐藏。meta.activeMenu
:激活菜单。meta.breadcrumb
:面包屑信息。
1.4 动态路由匹配
动态路由匹配是指根据当前路由路径,动态匹配出对应的路由组件。在 Vue Router
中,我们可以使用 :id
等动态参数来匹配路由路径。
import User from './User.vue'
const routes = [
{ path: '/users/:id', component: User },
]
路径参数 用冒号 :
表示。当一个路由被匹配时,它的 params
的值将在每个组件中以 route.params
的形式暴露出来。
template>
div>
User {{$route.params.id}}
div>
template>
可以在同一个路由中设置有多个 路径参数,它们会映射到 $route.params
上的相应字段
匹配模式 | 匹配路径 | $route.params |
---|---|---|
/users/:username |
/users/ayla |
{username: 'ayla'} |
/users/:username/ids/:id |
/users/ayla/ids/001 |
{username: 'ayla', id: '001'} |
常规参数只匹配 url
片段之间的字符,用 /
分隔。如果想 匹配任意路径 ,我们可以使用自定义的 路径参数 正则表达式,在 路径参数 后面的括号中加入 正则表达式 :
const routes = [
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
{ path: '/user-:afterUser(.*)', component: UserGeneric },
]
更多的动态路由匹配规则,可以参考 vue router 4
官方文档 路由的匹配语法 部分
注意 :
使用带有参数的路由时,当用户从/users/johnny
导航到/users/marry
时,相同的组件实例将被重复使用(因为两个路由都渲染同个组件,比起销毁再创建,复用会更加高效)。不过,这也意味着组件的生命周期钩子不会被调用。
若要对同一个组件中参数的变化做出响应的话,可以watch
$route.params
或者在路由守卫beforeRouteUpdate
做出响应。
1.5 路由传参
1.5.1 query 参数
传递参数
router-link to="/news/detail?a=1&b=2&content= 欢迎你">
跳转
router-link>
RouterLink
:to="{
//name:'detail', // 用 name 也可以跳转
path:'/news/detail',
query:{
id:news.id,
title:news.title,
content:news.content
}
}"
>
{{news.title}}
RouterLink>
接收参数
import {useRoute} from 'vue-router'
const route = useRoute()
console.log(route.query)
1.5.2 params 参数
传递参数
RouterLink :to="`/news/detail/001/ 新闻 001/ 内容 001`">{{news.title}}RouterLink>
RouterLink
:to="{
name:'detail', // 用 name 跳转
params:{
id:news.id,
title:news.title,
content:news.title
}
}"
>
{{news.title}}
RouterLink>
接收参数
import {useRoute} from 'vue-router'
const route = useRoute()
console.log(route.params)
1.5.3 路由的 props 配置
可以将路由参数作为 props
传给组件
const routes = [{
name: 'detail',
path: 'detail/:id/:title/:content',
component: Detail,
props: route => ({ id: route.params.id, sort: route.query.sort })
}]
接收参数
template>
div>
p>User ID: {{ id }}/p>
p>Sort Order: {{ sort }}/p>
/div>
/template>
script setup>
defineProps(['id', 'sort']);
/script>
1.6 导航方式
声明式 | 编程式 |
---|---|
|
router.push(...) |
-
编程式导航 :通过
JavaScript
代码来进行页面的跳转和切换。Vue Router
提供了一些方法来实现这种导航方式,如router.push
、router.replace
和router.go
。 -
声明式导航 :通过在模板中声明的方式来进行页面的切换和跳转。在
Vue Router
中,可以使用
组件来实现声明式导航。
组件可以被渲染为一个标签,用于通过路由链接跳转页面。例如:
Home
router.push
:会向 history
栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL
。
router.push('/users/eduardo')
router.push({ path: '/users/eduardo' })
router.push({ name: 'user', params: { username: 'eduardo' } })
router.push({ path: '/register', query: { plan: 'private' } })
router.push({ path: '/about', hash: '#team' })
router.replace
:类似于 router.push
,唯一不同的是,它在导航时不会向 history
添加新记录。
router.push({ path: '/home', replace: true })
router.replace({ path: '/home' })
router.go
:该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步。
router.go(1)
router.go(-1)
router.go(3)
router.go(-100)
router.go(100)
1.7 历史模式
在创建路由器实例时,history
配置允许我们在不同的历史模式中进行选择。
Hash
模式:createWebHashHistory()
– 地址带#
号,不需要服务器配置,不利于SEO
。HTML5
模式:createWebHistory()
– 不带#
号,需要适当的服务器配置,否则刷新会有 404 错误。Memory
模式:createMemoryHistory()
– 不会假定自己处于浏览器环境,不会与URL
交互也不会自动触发初始导航,不能前进或后退,适合Node
环境和SSR
。
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
],
})
1.8 导航守卫
a. 全局前置守卫 (beforeEach)
b. 全局解析守卫 (beforeResolve)
c. 全局后置钩子 (afterEach)
d. 路由独享守卫 (beforeEnter)
e. 组件内的守卫 (beforeRouteEnter, beforeRouteUpdate, beforeRouteLeave)
1.8.1 全局前置守卫
router.beforeEach()
:全局前置守卫是最常用的守卫之一。当一个导航触发时,全局前置守卫会按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve
完之前一直处于等待中。
router.beforeEach(async (to, from) => {
if (
!isAuthenticated &&
to.name !== 'Login'
) {
return { name: 'Login' }
}
})
参数:
to
:即将跳转的路由from
:当前导航正要离开的路由next(可选)
: 在之前的Vue Router
版本中,还可以使用 第三个参数next
。这是一个常见的错误来源,vue
官方经过RFC
讨论将其移除。然而,目前next
仍然可以使用,这意味着你可以向任何导航守卫传递第三个参数。在这种情况下,需要确保next
在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。
返回值:
false
: 取消当前的导航。如果浏览器的URL
改变了(可能是用户手动或者浏览器后退按钮),那么URL
地址会重置到from
路由对应的地址。- 一个路由地址:通过一个路由地址重定向到一个不同的地址,如同调用
router.push()
,且可以传入诸如replace: true
或name: 'home'
之类的选项。它会中断当前的导航,同时用相同的from
创建一个新导航。undefined
或者true
:导航是有效的,之后会按流程调用下一个导航守卫。
1.8.2 全局解析守卫
router.beforeResolve()
:与 router.beforeEach
类似,在每次导航时都会触发,不同的是,解析守卫刚好会在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。
因此,router.beforeResolve
是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
return false
} else {
throw error
}
}
}
})
1.8.3 全局后置钩子
router.afterEach()
:与前置守卫不同的是,全局后置钩子不会接受 next
函数也不会改变导航本身。
利用它可以完成分析、更改页面标题、声明页面等辅助功能。
router.afterEach((to, from) => {
sendToAnalytics(to.fullPath)
})
1.8.4 路由独享守卫
beforeEnter
:路由独享守卫是针对单个路由的守卫,可以定义在路由配置中。
beforeEnter
守卫 只在进入路由时触发,不会在 params
、query
或 hash
改变时触发。例如,从 /users/2
进入到 /users/3
或者从 /users/2#info
进入到 /users/2#projects
。它们只有在 从一个不同的 路由导航时,才会被触发。
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/protected',
beforeEnter: (to, from, next) => {
if (store.getters.isLoggedIn) {
next();
} else {
next('/login');
}
},
component: ProtectedComponent
}
]
});
1.8.5 组件内的守卫
组件级别的守卫可以定义在组件内,包括进入前守卫、更新前守卫和离开前守卫。
beforeRouteEnter
:在进入路由之前被调用。beforeRouteUpdate
:在当前路由改变,但是该组件被复用时调用。beforeRouteLeave
:在离开路由之前被调用。
script>
export default {
beforeRouteEnter(to, from) {
},
beforeRouteUpdate(to, from) {
},
beforeRouteLeave(to, from) {
},
}
/script>
1.9 动态路由
对路由的添加通常是通过 routes
选项来完成的,但是在某些情况下,可能需要在应用程序已经运行的时候添加或删除路由。
1.9.1 添加路由
假设目前只有一个路由:
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/:articleName', component: Article }],
})
此时,进入任何页面,例如 /about
或 /article
,都会被匹配到 Article
页面。若此时利用 router.addroute
添加/about
路由:
router.addRoute({ path: '/about', component: About })
页面仍然会显示 Article
组件,我们需要手动调用 router.replace()
来改变当前的位置,并覆盖原来的位置:
router.addRoute({ path: '/about', component: About })
router.replace(router.currentRoute.value.fullPath)
*注意:如果需要等待新的路由显示,可以使用 await router.replace()
。
如果需要在导航守卫内部添加或删除路由,不应该调用 router.replace()
,而是要通过返回新的位置来触发重定向:
router.beforeEach(to => {
if (!hasNecessaryRoute(to)) {
router.addRoute(generateRoute(to))
return to.fullPath
}
})
1.9.2 删除路由
- 通过添加一个名称冲突的路由。如果添加与现有途径名称相同的途径,会先删除路由,再添加路由:
router.addRoute({ path: '/about', name: 'about', component: About })
router.addRoute({ path: '/other', name: 'about', component: Other })
- 通过调用 router.addRoute() 返回的回调:
const removeRoute = router.addRoute(routeRecord)
removeRoute()
- 通过使用 router.removeRoute() 按名称删除路由:
router.addRoute({ path: '/about', name: 'about', component: About })
router.removeRoute('about')
*注意:当路由被删除时,所有的别名和子路由也会被同时删除
1.9.3 添加嵌套路由
要将嵌套路由添加到现有的路由中,可以将路由的 name 作为第一个参数传递给 router.addRoute()
router.addRoute({ name: 'admin', path: '/admin', component: Admin })
router.addRoute('admin', { path: 'settings', component: AdminSettings })
router.addRoute({
name: 'admin',
path: '/admin',
component: Admin,
children: [{ path: 'settings', component: AdminSettings }],
})
1.9.4 查看现有路由
router.hasRoute():检查路由是否存在。router.getRoutes():获取一个包含所有路由记录的数组。
1.10 router 与 route
router
是Vue Router
的实例,它代表了整个路由系统。可以在组件的 setup 函数中通过useRouter
函数获取到这个实例。router
实例提供了全局的路由控制功能,如编程式导航(使用push
、replace
等方法)、监听路由变化(通过beforeEach
、afterEach
等钩子函数)等。
route
是当前激活的路由信息对象,它包含了当前URL
解析得到的信息。可以在组件的setup
函数中通过useRoute
函数获取到这个对象。route
对象用于访问当前路由的状态信息,如路径、查询参数、哈希值、路由参数等。
router 实例的属性和方法
import { useRouter } from 'vue-router'
const router = useRouter()
router.options
router.currentRoute
router.listening
router.addRoute(name, route)
router.afterEach(to, from)
router.beforeEach(to, from)
router.beforeResolve(to, from)
router.clearRoutes()
router.removeRoute(name)
router.hasRoute(name)
router.getRoutes()
router.go(n)
router.back()
router.forward()
router.push(to)
router.replace(to)
router.install(app)
router.resolve(to, currentLocation?)
router.isReady()
router.onError(errorHandler)
route 对象的属性
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.fullPath)
console.log(route.hash)
console.log(route.matched)
console.log(route.meta)
console.log(route.name)
console.log(route.params)
console.log(route.path)
console.log(route.query)
console.log(route.redirectedFrom)
内容参考:
[1] vue router 4 官方教程
原文地址: Vue router 4 基础知识讲解