💡简介

来自官网 https://router.vuejs.org/zh/guide/

Vue 让我们用组件组成了应用,而 Vue Router 又能将组件映射到路由上。

  1. router.js 创建路由,同时将组件映射到路由上

  2. main.js 引入 router.js,并 app.use(router)

  3. 在模板中使用,用到了两个组件 <router-view><router-link>

<nav>
    <!-- 使用 router-link 组件进行导航 -->
    <!-- 通过传递 `to` 来指定链接 -->
    <!-- `<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
    <router-link to="/">Go to Home</router-link>
    <router-link to="/about">Go to About</router-link>
</nav>

<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>

1. 动态路由

在路径中使用一个动态字段(路径参数),路径参数用 : 表示,其值可通过 $route.params 来访问。比如:

  • /users/:id

  • /users/:username

  • /users/:username/posts/:postId

1.1 参数变化时

当动态参数发生变化时,相同的组件实例会被重复利用(比起销毁再重建更高效),这就意味着组件的生命周期钩子不会被调用。

1.2 相应参数变化

此时如果要响应同一个组件中的参数变化,可以有以下办法:

  1. watch this.$route 对象的任意属性

    • Vue3 在 setup 里没有 this,所以是使用 useRouter, useRoute 函数

  2. 使用组件内导航守卫 beforeRouteLeave, beforeRouteUpdate

    • Vue3 用了组合式 API,所以是 onBeforeRouteLeave, onBeforeRouteUpdate 组合式函数 API

  3. <router-view :key="$route.path"> 是强制刷新组件(先 destroy 再 re-render)

import { useRouter, useRoute, onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

const router = useRouter()
const route = useRoute()  // 避免监听整个 route 对象,应该直接监听期望改变的参数

// 在模板中可以访问 $router 和 $route

2. 嵌套路由

要将组件渲染到嵌套的 router-view 中,就需要在路由中配置 children

  • 几个 children 就应该有几个 <RouterView />

  • 空的嵌套路径,也很有用,{ path: '' }

  • 通常会给路由命名,使用方便,name 属性

  • 跳转,跳父路由 vs 跳子路由

/ 开头的嵌套路径将被视为根路径,这将允许我们利用组件嵌套,而不必使用嵌套的 URL。

3. 路由守卫

Navigation Guards

  • 场景:普通路由、动态路由(对参数有效性的校验)

  • 用途:需要登录的页面、NotFoundPage,详见文末示例

  • 路由守卫的几种方式:

    • 全局

    • xxx

4. 历史模式

History mode:HTML5 模式 vs Hash 模式

  • HTML5 web history API,Single Page App

  • /#/home 传统的 server side apps

5. 性能方面

5.1 懒加载(动态导入)

表现是最终打包的资源是否分包 ,写法是 dynamic import 路由的组件。

对应内容:bundle 源代码,每个 page 都有它自己的 chunk/file/bundle。

  • Vite 的 code splitting 特性

    • 开发模式下,Vite 没有 bundle 代码,直接用的浏览器原生的 import

  • Webpack 的 code splitting 特性

    • 在 Vue 中可以用 magic comment 给每个组件对应的小 bundles 命名

5.2 路由的草稿功能

切换路由时,保持草稿功能

<router-view v-slot="{ Component }">
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
</router-view>

6. 更多

6.1 更多特性

  1. 命名路由:没有硬编码的 URL、绕过路径排序

    • <router-link :to="{name: 'user'}">

    • router.push({name: 'user'})

  2. 路由的 redirectalias。很实用的样子,详见下方示例

  3. 给路由传参:props 属性

  4. 路由的匹配语法

    • 静态路由

    • 动态路由

    • 也可以使用正则表达式 { path: '/xx/:id(\\d+)' }

  5. 动态增加路由,用脚本 add/remove 的 route record(比如某些插件)

  6. 从整体配置哪些路由要特定组件(比如页面广告)

    • Named View, <router-view name="xxx"></router-view>

    • 路由配置用了 components 属性,而不是 component

  7. 路由组件切换时

    • 动画:全局 Vue 组件 <transition><transition>

    • 滚动行为,scrollBehavior 属性

<!-- Vue2 动画 -->
<transition>
    <router-view></router-view>
<transition>

<!-- Vue3 动画 -->
<router-view v-slot="{Component}">
    <!-- name 指定 css 的类名,  slide | moveUp | fade,  back-in -->
    <transition name="slide" mode="out-in"> 
        <component :is="Component"></component>
    </transition>
</router-view>

6.2 注意事项

  1. <RouterLink> vs <a>

    • 前者只刷新路由组件,它会拦截 click 事件。适合内部链接

    • 后者会 reload 整个页面,适合外部链接

  2. 当前选中的路由,有类名 .router-link-acitive,也可以用 linkActiveClass 自定义

  3. 路由跳转

    • 在模板里用 <router-link>

    • 在脚本里用 $router.push, $router.replace。不同之处是如何影响路由历史

  4. routefullPathpath

    • fullPath 包括 path, query, hash

    • path 包括 path

  5. components 文件夹放整个 App 的组件,若是某个 page 的则放自己 views/page/components

  6. 监测路由失败 import { isNavigationFailure, NavigationFailureType } from 'vue-router' 失败的原因可能如下图

7. 示例

const routes = [
  {
    path: '/',
    component: () => import('@/views/HomeView.vue'),
    children: [
      {
        path: '',
        alias: ['/home', '/search'],
        name: 'ResultList',
        component: () => import('@/views/home/ResultListView.vue')
      },
      {
        path: 'preview/:id',
        name: 'CardPreview',
        component: () => import('@/views/home/CardPreview.vue')
      },
      {
        path: 'feature/:cat',
        name: 'FeatureList',
        component: () => import('@/views/home/FeatureView.vue'),
        beforeEnter: (to, from) => {
          const cat = to.params.cat
          if (cat === 'html' || cat === 'css') {
          } else {
            return { name: 'NotFound' }
          }
        }
      }
    ]
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/LoginView.vue')
  },
  {
    path: '/admin',
    component: () => import('@/views/AdminView.vue'),
    meta: {
      requiresAuth: true
    },
    children: [
      {
        path: '',
        name: 'AdminList',
        component: () => import('@/views/admin/ListView.vue')
      },
      {
        path: 'card',
        name: 'AdminCard',
        component: () => import('@/views/admin/CardView.vue')
      }
    ]
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('@/views/NotFound.vue')
  }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

方法

  1. install()

  2. isReady()

  3. onError() 错误处理程序

  1. back: () => go(-1)

  2. forward: () => go(1)

  3. go()

  4. push()

  5. replace()

  1. beforeResolve() 导航守卫

  2. beforeEach() 导航守卫

  3. afterEach() 导航钩子

  1. getRoutes() 路由记录的完整列表

  2. hasRoute()

  3. resolve() 返回路由地址的标准化版本

  1. addRoute() 动态添加/删除路由

  2. removeRoute()

属性

  1. currentRoute

  2. options

  3. listening

  4. __hasDevtools

最重要的是了解原理,因为具体的写法和某些约定可能会变(知识本身在更新)。

  • 深入的:原理、实现

  • 宽泛的:有几种方式,它们的适用场景和特点

有关路由的详细参数,详见 Vue Router API 参考

Last updated