banner
banner
banner
NEWS LETTER

VUE3-路由

Scroll down

路由标签

  • router-link:to属性指定组件的链接
  • router-view:路由出口,to指定的路由组件渲染在该标签内

路由模式

  • 由mode改为history,并且属性值也调整
    • history模式:history: createWebHistory()
    • hash模式:history: createWebHashHistory()
    • abstract模式:history: createMemoryHistory()

创建一个路由文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.引入路由api
import { createRouter, createWebHistory } from 'vue-router'
2.定义路由组件或引入路由组件
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
3.配置路由表跟vue2一样
const routes = [{
path: '/about/:id', //:params,代表动态路径,使用this.$route.params来调用
component: About
children: [ // 嵌套路由
{
path: 'profile', // 当/about/:id/profile匹配成功就渲染
name: 'about', // 只有子路由具有名称(命名路由)
component: Profile
}
]
},]
4.创建路由实例并传递routes配置,
- vue2使用new VueRouter
- vue3使用VueRouter.createRouter
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory()
routes,
})
  • 在main.js文件创建并挂载根实例
    1
    2
    3
    const app = Vue.createApp({})
    app.use(router)
    app.mount('#app')
  • 在组件页面中使用:在任意组件中以 this.$router 的形式访问全局的router,以this.$route 的形式访问当前组件的路由,在setup() 函数中访问路由,可以调用useRouter 或 useRoute 函数

响应路由参数的变化

  • 带参的路由导航到另一个带参路由,相同的组件实例将被重复使用,也意味着组件的生命周期钩子不会被调用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const User = {
    template: '<template>{{ $route.params.id }}<template>'
    created(){
    this.$watch(
    ()=> this.$route.params,
    (toParams, previousParams) => {
    // 对路由变化做出响应
    }
    )
    }
  • beforeRouteUpdate(to,from){}:取消导航(搭配async/await)

路由的匹配语法

  • 匹配所有内容放在$route.params.xxx下

    • 示例:{ path: '/:xxx(.*)*', name: 'NotFound', component: NotFound }
  • 匹配以/xxx开头的所有内容,并将其放在$route.params.afterUser下

    • 示例:{ path: '/xxx:afterUser(.*)',component: UserGeneric }
  • 在参数中自定义正则 — 适用范围区分同一路径不同参数的路由

1
2
3
4
5
6
7
8
9
10
const routes = [
// 匹配 /o/3549
{ path: '/o/:orderId' },
// 匹配 /p/books
{ path: '/p/:productName' },
// /:orderId -> 仅匹配数字
{ path: '/:orderId(\\d+)' },
// /:productName -> 匹配其他任何内容
{ path: '/:productName' }
]
  • 可重复的参数

    • 在参数后面添加*表示匹配(0~n)个参数:{ path: ‘/:chapters*’ }
    • 在参数后面添加+表示匹配(1~n)个参数:{ path: ‘/:chapters+’ }
      • 使用命名路由时该参数需要传递一个数组

Sensitive 与 strict 路由配置

  • 当 strict: true 时,只能匹配不带有尾部斜线的路由(/xx/)
  • 当 sensitive: true 时,区分大小写
  • 当前路由上的 strict 或 sensitive 可以覆盖全局
    1
    2
    3
    4
    5
    6
    7
    const router = createRouter({
    routes: [
    { path: '/users/:id?', sensitive: true },
    // ?将参数(0个或一个)标记为可选,且参数不能重复
    ]
    strict: true, // 应用于全部路由
    })

编程式导航(可参考vue2)

字符串路径

  • router.push(‘/users/eduardo’)

带有路径的对象

  • router.push({ path: ‘/users/eduardo’ })

命名的路由,并加上参数

  • 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.push — 向history栈添加一个新记录

router.replace — 类似router.push,但不会向history添加新记录

router.go(整数作参) — 表示在历史堆前进或退后几步

  • router.go(1) // 前进一条记录
  • router.go(-1) // 返回一条记录

命名视图

  • 同时展示多个视图,而不是嵌套展示。用于同一路由多个视图
  • 在路由里面的components配置好多个组件,
  • 然后页面的使用多个router-view标签,带有name属性的会跟默认视图同一级渲染,不带的则是嵌套渲染在同一个router-view标签,注意name的值要和组件的名字一样
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <router-view class="view main-content"></router-view>
    <router-view class="view right-sidebar" name="RightSidebar"></router-view>
    const router = createRouter({
    history: createWebHashHistory(),
    routes: [{
    path: '/',
    components: {
    default: Home,
    LeftSidebar,
    RightSidebar,// 它们与 `<router-view>` 上的 `name` 属性匹配
    },
    },],
    })

重定向和别名

  • 配置在路由路由表上的路由

redirect: 重定向

  • 直接写路径:redirect: ‘/‘
  • 重定向到命名路由:redirect: { name: ‘homepage’ }
  • 重定向一个方法,动态:
    1
    2
    3
    4
    5
    6
    const routes = [{ 
    path: '/search/:searchText',
    redirect: to => { // 可以省略component
    return { path: 'search', query: { q: to.params.searchText } }
    }
    }]

alias:给路径起别名

相对重定向

1
2
3
4
5
6
7
8
9
10
11
const routes = [
{
// 将/users/123/posts重定向到/users/123/profile。
path: '/users/:id/posts',
redirect: to => {
// 相对位置不以`/`开头
// 或 { path: 'profile'}
return 'profile'
},
},
]

将 props 传递给路由组件

  • 在路由表上的路由配置,路由参数和prop的参数名要相同
  • 以数组形式传递:props: [‘id’]
  • 以布尔值传递:把路由收到的所有params参数通过props传给该路由组件
    • 示例:path:’/user/:params’,props:true,
  • 为每个有命名视图的路由定义props
    • props: { default: true, 命名视图路由名: false }
  • 函数模式:传递query参数通过props传递给组件
    • 示例:props: route => ({ query: route.query.q })

导航守卫

  • 全局前置守卫 – router.beforeEach
  • 全局解析守卫 – router.beforeResolve
  • 全局后置钩子 – router.afterEach
  • 路由独享的守卫 – routes 里面 beforeEnter 属性
  • 组件内的守卫 – 在组件页面中使用的钩子:{beforeRouteEnter(){},beforeRouteUpdate(){},beforeRouteLeave(){}}

钩子的参数解析

  • to: 即将要进入的目标
  • from: 当前导航正要离开的路由
  • next:多调用一次
  • isAuthenticated:用户是否登录过
  • to.name !== ‘路由名’ : 避免无限重定向
  • return false: 取消导航

全局前置守卫

  • 当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve完之前一直处于等待中
    1
    2
    3
    4
    router.beforeEach((to, from, next) => {
    if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
    else next()
    })

全局解析守卫

  • 每次导航时都会触发,但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用。是获取数据执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    router.beforeResolve(async to => {
    if (to.meta.requiresCamera) {
    try {
    await askForCameraPermission()
    } catch (error) {
    if (error instanceof NotAllowedError) {
    // ... 处理错误,然后取消导航
    return false
    } else {
    // 意料之外的错误,取消导航并把错误传给全局处理器
    throw error
    }
    }
    }
    })

全局后置钩子

  • 不接受next 函数也不改变导航本身,对于分析、更改页面标题、声明页面等辅助功能有用
    1
    2
    3
    router.afterEach((to, from, failure) => {
    if (!failure) sendToAnalytics(to.fullPath)
    })

路由独享的守卫

  • 只在进入路由时触发,不会在params、query或hash改变时触发,只有在一个不同的路由导航时,才会被触发。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const routes = [
    {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from) => {
    // reject the navigation
    return false
    },
    },
    ]

组件内的守卫

  • beforeRouteEnter(to, from, next) {}
    • 在渲染该组件的对应路由被验证前调用
    • 不能获取组件实例this
    • 因为当守卫执行时,组件实例还没被创建!
      1
      2
      3
      4
      5
      beforeRouteEnter(to, from, next) {
      next(vm => {
      /* 通过vm访问组件实例*/
      })
      }
  • beforeRouteUpdate(to, from) {}
    • 在当前路由改变,但是该组件被复用时调用
    • 举例: 对于一个带有动态参数的路径 /users/:id,在 /users/1/users/2 之间跳转的时候,
    • 由于会渲染同样的 UserDetails 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    • 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 this
  • beforeRouteLeave(to, from) {}
    • 在导航离开渲染该组件的对应路由时调用
    • beforeRouteUpdate 一样,它可以访问组件实例 this
    • 通常用来预防用户在还未保存修改前突然离开
      1
      2
      3
      4
      beforeRouteLeave(to, from) {
      const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
      if (!answer) return false
      }

导航解析流程

  1. 导航被触发
  2. 失活的组件里调用 beforeRouteLeave 守卫
  3. 调用全局的 beforeEach 守卫
  4. 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫(2.5+)
  9. 导航被确认
  10. 调用全局的 afterEach 钩子
  11. 触发 DOM 更新
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入

路由元信息 — meta 属性添加附加信息,调用$route.meta

数据获取

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
  • 导航完成后获取数据会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。
    • 在created()中watch路由参数,调用methods中的状态,展示一些状态,当然这些状态要在data中初始化
  • 在导航完成前获取数据导航转入新的路由前获取数据
    • beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。建议在数据获取期间,显示一些进度条或者别的指示

Vue Router 和 组合式API(在setup中使用)

  • 访问路由和当前路由:使用useRouter和useRoute替代this.$router和this.$route

组件内的API导航守卫

  • 组合式API守卫可以用在任何<router-view>渲染的组件,不必像组件内守卫那样直接用在路由组件上
  • 引入:import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
  • 除了不能访问this其它一样
  • 页面存在router-link标签<router-link v-solt="{props1, props2, props3}">
  • 编码时引入import { RouterLink, useLink } from 'vue-router'
  • 在props配置项中使用...RouterLink.props 传入router-link标签上的props
  • 在setup中传入propssetup(props){}
  • 拿到props: 在setup中使用useLink(props)并通过解构的方式拿到

过渡动效

  • 在路径组件上使用转场,并对导航进行动画处理

    • 示例:
      1
      2
      3
      4
      5
      <router-view v-slot="{ Component }">
      <transition name="fade">
      <component :is="Component" />
      </transition>
      </router-view>
  • 单个路由的过渡 — 元信息meta和上的动态的name

    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      const routes = [{
      path: '/custom-transition',
      component: PanelLeft,
      meta: { transition: 'slide-left' },
      },]
      <router-view v-slot="{ Component, route }">
      <transition :name="route.meta.transition || 'fade' ">
      <component :is="Component" />
      </transition>
      </router-view>
  • 基于路由的动态过渡 — 根据路径的深度动态添加信息到 meta 字段

    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <router-view v-slot="{ Component, route }">
      <transition :name="route.meta.transition">
      <component :is="Component" />
      </transition>
      </router-view>
      router.afterEach((to, from) => {
      const toDepth = to.path.split('/').length
      const fromDepth = from.path.split('/').length
      to.meta.transition = toDepth < fromDepth ? 'slide-right' : 'slide-left'
      })
  • 强制在复用的视图之间进行过渡

    • 在component标签上再添加一个key属性来强制过渡

滚动行为

  • 只在支持 history.pushState 的浏览器中可用

创建router时提供scrollBehavior(to, from, savedPosition){}方法

  • savedPosition由浏览器的后退/前进按钮触发
  • return返回期望滚到那个位置
  • 始终滚动到顶部:return { top: 0 }
  • 不滚动:return false | return null
  • 按下后退/前进按钮时就会像浏览器原生表现: return savedPosition

可以返回一个ScrollToOptions位置对象

通过el传递一个CSS选择器或者一个DOM元素,此时top和left将被视为该元素的相对偏移量

  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    const router = createRouter({
    scrollBehavior(to, from, savedPosition) {
    return {
    el: '#main',
    top: -10,
    }
    },
    })

滚动到锚点行为

  • return 时添加behavior: ‘smooth’这个配置

延迟滚动

  • return new Promise((resolve, reject) => {延时器}),可以返回所需的位置描述符

路由懒加载

动态导入代替静态导入

  • 不使用import…from…使用 const 组件名 = () => import(‘路径’)
  • 路由配置的component接受一个返回Promise组件的函数,vue router只会在第一次进入页面时才会获取这个函数,然后使用缓存数据,不要在路由中使用异步组件
    • 示例:
      1
      2
      3
      const UserDetail = () => {
      Promise.resolve({/*组件定义*/})
      }

把组件按组分块

  • 使用webpack把某个路由下的所有组件都打包在同个异步块chunk中
    • webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中
    • 即使用动态路径导入多个路径不同组件名相同的文件
  • 使用Vite
    • 在vite.config.js文件下的build –> rollupOptions –> output –> manualChunks中配置
    • 以’组名’: [‘路径1’,’路径2’,’路径3’]的形式添加到manualChunks中

导航故障

  • 等待导航结果

    • 导航是异步的,返回Promise:await router.push(‘/my-profile’)
    • 导航到新页面后隐藏菜单:this.isMenuOpen = false
  • 检测导航故障 — 失败返回false

    • 引入import { NavigationFailureType, isNavigationFailure } from ‘vue-router’
    • 将异步导航赋值给一个变量
    • 检测试图离开未保存的编辑文本界面并在其中对用户进行提示操作:
      • if(isNavigationFailure(failure, NavigationFailureType.aborted)){}
  • 鉴别导航故障

    • aborted:在导航守卫中返回false中断了本次导航
    • cancelled:在当前导航还没有完成之前又有了一个新的导航
    • duplicated:导航被阻止,因为我们已经在目标位置了
  • 导航故障属性

    • 所有的导航失败都会暴露 to 和 from 属性
    • failure.to.path:反映失败导航的当前位置
    • failure.from.path:反映失败导航的目标位置
  • 检测重定向 — 不会阻止导航,而是创建一个新导航

    • redirectedFrom 是解析出的路由地址,就像导航守卫中的 to和 from
    • 示例:if (router.currentRoute.value.redirectedFrom) {}

动态路由

  • 查看现有路由

    • 检查路由是否存在:router.hasRoute()
    • 获取一个包含所有路由记录的数组:router.getRoutes()
  • 添加嵌套路由

    • 使用router.addRoute来添加路由
    • 示例:
      1
      2
      router.addRoute({ name: 'admin', path: '/admin', component: Admin })
      router.addRoute('admin', { path: 'settings', component: AdminSettings })
  • 删除路由 — 所有别名和子路由也会被删除

    • 方法一:使用router.addRoute添加一个name相同的路由
    • 方法二:调用router.addRoute返回的回调,存在就删除
    • 方法三:使用router.removeRoute(‘name’)
    • 注:当路由被删除时,所有的别名和子路由也会被同时删除
  • 在导航守卫中添加路由

    • hasNecessaryRoute():在添加新的路由后返回 false,以避免无限重定向
其他文章
cover
VUE3-VUEX(状态管理库)
  • 24/11/01
  • 14:39
  • VUE
cover
VUE3-快速上手
  • 24/11/01
  • 14:39
  • VUE
目录导航 置顶
  1. 1. 路由标签
  2. 2. 路由模式
  3. 3. 创建一个路由文件
  4. 4. 响应路由参数的变化
  5. 5. 路由的匹配语法
    1. 5.1. 匹配所有内容放在$route.params.xxx下
    2. 5.2. 匹配以/xxx开头的所有内容,并将其放在$route.params.afterUser下
    3. 5.3. 在参数中自定义正则 — 适用范围区分同一路径不同参数的路由
    4. 5.4. 可重复的参数
  6. 6. Sensitive 与 strict 路由配置
  7. 7. 编程式导航(可参考vue2)
    1. 7.1. 字符串路径
    2. 7.2. 带有路径的对象
    3. 7.3. 命名的路由,并加上参数
    4. 7.4. 带查询参数,结果是 /register?plan=private
    5. 7.5. 带 hash,结果是 /about#team
    6. 7.6. router.push — 向history栈添加一个新记录
    7. 7.7. router.replace — 类似router.push,但不会向history添加新记录
    8. 7.8. router.go(整数作参) — 表示在历史堆前进或退后几步
  8. 8. 命名视图
  9. 9. 重定向和别名
    1. 9.1. redirect: 重定向
    2. 9.2. alias:给路径起别名
    3. 9.3. 相对重定向
  10. 10. 将 props 传递给路由组件
  11. 11. 导航守卫
    1. 11.1. 钩子的参数解析
    2. 11.2. 全局前置守卫
    3. 11.3. 全局解析守卫
    4. 11.4. 全局后置钩子
    5. 11.5. 路由独享的守卫
    6. 11.6. 组件内的守卫
    7. 11.7. 导航解析流程
    8. 11.8. 路由元信息 — meta 属性添加附加信息,调用$route.meta
    9. 11.9. 数据获取
  12. 12. Vue Router 和 组合式API(在setup中使用)
    1. 12.1. 组件内的API导航守卫
    2. 12.2. useLink / 扩展RouterLink
  13. 13. 过渡动效
    1. 13.1. 在路径组件上使用转场,并对导航进行动画处理
    2. 13.2. 单个路由的过渡 — 元信息meta和上的动态的name
    3. 13.3. 基于路由的动态过渡 — 根据路径的深度动态添加信息到 meta 字段
    4. 13.4. 强制在复用的视图之间进行过渡
  14. 14. 滚动行为
    1. 14.1. 创建router时提供scrollBehavior(to, from, savedPosition){}方法
    2. 14.2. 可以返回一个ScrollToOptions位置对象
    3. 14.3. 通过el传递一个CSS选择器或者一个DOM元素,此时top和left将被视为该元素的相对偏移量
    4. 14.4. 滚动到锚点行为
    5. 14.5. 延迟滚动
  15. 15. 路由懒加载
    1. 15.1. 动态导入代替静态导入
    2. 15.2. 把组件按组分块
  16. 16. 导航故障
    1. 16.1. 等待导航结果
    2. 16.2. 检测导航故障 — 失败返回false
    3. 16.3. 鉴别导航故障
    4. 16.4. 导航故障属性
    5. 16.5. 检测重定向 — 不会阻止导航,而是创建一个新导航
  17. 17. 动态路由
    1. 17.1. 查看现有路由
    2. 17.2. 添加嵌套路由
    3. 17.3. 删除路由 — 所有别名和子路由也会被删除
    4. 17.4. 在导航守卫中添加路由
请输入关键词进行搜索