首页 > 其他分享 >Vue router 4 基础知识讲解

Vue router 4 基础知识讲解

时间:2024-09-24 18:23:42浏览次数:8  
标签:Vue 基础知识 守卫 path 组件 router 导航 路由

1. Vue router 4 基础

在构建现代 Web 应用时,单页应用(SPA)因其流畅的用户体验和快速的页面切换能力,成为了众多项目的首选架构。
然而,在SPA中,随着应用功能的日益复杂,权限控制成为了一个不可忽视的问题。如何确保不同用户只能访问其被授权的资源,是保障应用安全和数据一致性的关键。
在这个过程中,路由管理扮演着至关重要的角色。 Vue Router ,作为 Vue 官方提供的路由管理器,不仅能够帮助我们定义应用的页面结构和导航逻辑,还能与权限控制机制紧密结合,实现细粒度的访问控制。
Vue3 项目中,通过 Vue Router 的路由守卫功能,我们可以在用户访问某个路由之前进行权限验证。例如,我们可以利用全局前置守卫(beforeEach)来检查用户的登录状态和权限级别,从而决定是否允许用户继续访问目标页面。如果用户未登录或权限不足,我们可以重定向用户到登录页面或提示页面,确保应用的安全性。
此外, Vue Router 还支持路由元信息( meta 字段),这为我们在路由层面定义额外的信息提供了便利。通过路由元信息,我们可以为不同的路由设置不同的权限要求,并在路由守卫中根据这些信息进行权限验证。这种方式使得权限控制更加灵活和可配置。

1.1 安装

可以使用npm包管理器直接安装,或者在创建新项目时,使用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()函数。

// v4.x 示例
// 1. 引入createRouter和createWebHistory 
import { createMemoryHistory, createRouter } from 'vue-router'

// 2. 引入路由组件
import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'

// 3. 定义路由
const routes = [
  { path: '/', component: HomeView },
  { path: '/about', component: AboutView },
]

// 4. 创建 router 实例并 export
const router = createRouter({
  history: createMemoryHistory(),
  routes,
})
export default router

// 5. 在main.js中挂载
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')

// 6. 在App.vue中使用RouterView来渲染路由组件。
// RouterView: 可以使 Vue Router 知道你想要在哪里渲染当前 URL 路径对应的路由组件。
<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 渲染的组件。应是一个具有与 components 相同键的对象,或是一个应用于所有组件的布尔值。
  • 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>
    <!-- 当前路由可以通过 $route 在模板中访问 -->
    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 = [
  // 将匹配所有内容并将其放在 `route.params.pathMatch` 下
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  // 将匹配以 `/user-` 开头的所有内容,并将其放在 `route.params.afterUser` 下
  { path: '/user-:afterUser(.*)', component: UserGeneric },
]

更多的动态路由匹配规则,可以参考vue router 4官方文档 路由的匹配语法 部分

注意
使用带有参数的路由时,当用户从 /users/johnny 导航到 /users/jolyne 时,相同的组件实例将被重复使用(因为两个路由都渲染同个组件,比起销毁再创建,复用会更加高效)。不过,这也意味着组件的生命周期钩子不会被调用。
若要对同一个组件中参数的变化做出响应的话,可以 watch $route.params 或者在路由守卫beforeRouteUpdate做出响应。

1.5 导航方式

声明式编程式
<router-link :to="...">router.push(...)
  • 声明式导航: 通过JavaScript代码来进行页面的跳转和切换。Vue Router提供了一些方法来实现这种导航方式,如router.pushrouter.replacerouter.go

  • 编程式导航:通过在模板中声明的方式来进行页面的切换和跳转。在Vue Router中,可以使用<router-link>组件来实现声明式导航。<router-link>组件可以被渲染为一个<a>标签,用于通过路由链接跳转页面。例如:<router-link to="/home">Home</router-link>

router push:会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL

// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

router replace:类似于 router.push,唯一不同的是,它在导航时不会向 history 添加新记录。

router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })

router.go:该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步。

// 向前移动一条记录,与 router.forward() 相同
router.go(1)
// 返回一条记录,与 router.back() 相同
router.go(-1)
// 前进 3 条记录
router.go(3)
// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)

1.6 历史模式

在创建路由器实例时,history 配置允许我们在不同的历史模式中进行选择。

  • Hash 模式: createWebHashHistory() - 地址带#号,不需要服务器配置,不利于SEO
  • HTML5 模式: createWebHistory() - 不带#号,需要适当的服务器配置,否则刷新会有404错误。
  • Memory 模式: createMemoryHistory() - 不会假定自己处于浏览器环境,不会与 URL 交互也不会自动触发初始导航,不能前进或后退,适合 Node 环境和 SSR
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(), // html5 模式
  routes: [
    //...
  ],
})

1.7 导航守卫

a. 全局前置守卫 (beforeEach)
b. 全局解析守卫 (beforeResolve)
c. 全局后置钩子 (afterEach)
d. 路由独享守卫 (beforeEnter)
e. 组件内的守卫 (beforeRouteEnter, beforeRouteUpdate, beforeRouteLeave)
1.7.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: truename: 'home' 之类的选项。它会中断当前的导航,同时用相同的 from 创建一个新导航。
  • undefined 或者 true:导航是有效的,之后会按流程调用下一个导航守卫。
1.7.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.7.3 全局后置钩子

router.afterEach():与前置守卫不同的是,全局后置钩子不会接受 next 函数也不会改变导航本身。

利用它可以完成分析、更改页面标题、声明页面等辅助功能。

router.afterEach((to, from) => {
  sendToAnalytics(to.fullPath)
})
1.7.4 路由独享守卫

beforeEnter:路由独享守卫是针对单个路由的守卫,可以定义在路由配置中。

beforeEnter 守卫 只在进入路由时触发,不会在 paramsqueryhash 改变时触发。例如,从 /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.7.5 组件内的守卫

组件级别的守卫可以定义在组件内,包括进入前守卫、更新前守卫和离开前守卫。

beforeRouteEnter:在进入路由之前被调用。
beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用。
beforeRouteLeave:在离开路由之前被调用。

<script>
export default {
  beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !
    // 因为当守卫执行时,组件实例还没被创建!
  },
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
    // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },
}
</script>

1.8 动态路由

对路由的添加通常是通过 routes 选项来完成的,但是在某些情况下,可能需要在应用程序已经运行的时候添加或删除路由。

1.8.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 })
// 我们也可以使用 this.$route 或 useRoute()
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.8.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.8.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.8.4 查看现有路由
router.hasRoute():检查路由是否存在。
router.getRoutes():获取一个包含所有路由记录的数组。

内容参考:
[1] vue router 4 官方教程

标签:Vue,基础知识,守卫,path,组件,router,导航,路由
From: https://blog.csdn.net/m0_46714106/article/details/142496647

相关文章