Vue Router 路由基础

前言

为了适配vue3,路由已经更新的v4版本了。与之前有不小的区别,因此打算按照官方文档,重新梳理学习一下,顺便巩固一下知识。

以下内容基于官方文档

Vue Router v4

安装

npm install vue-router@4

基础

入门

创建一个router目录
创建一个router目录;目录下创建一个src目录,用来放模块的路由;再创建一个index.ts 用来导出所有路由
在这里插入图片描述
index.ts

import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
//   练习模块
import test from "./src/test";
const history = createWebHistory();
const routes: Array<RouteRecordRaw> = [
  {
    //重定向,找不到路由时跳转到home
    path: "/:pathMatch(.*)*",
    redirect: "/home",
  },
  {
    path: "/",
    redirect: "/home",
  },
  {
    path: "/home",
    name: "home",
    component: () => import("../views/home.vue"),
  },
  {
    ...test,
  },
];
const router = createRouter({
  history,
  routes,
});
export default router;

createWebHistory也可以用createWebHashHistory替换,查了一下两者区别如下:

  • createWebHistory路由模式路径不带#号(生产环境下不能直接访问项目,需要nginx转发)
  • createWebHashHistory路由模式路径带#号

main.ts

import router from "./router/index";
app.use(router)

router-link 和 router-view

router-link

<router-link to="/">Go to Home</router-link>

创建链接,to 用来指向要跳转的路由;不过一般用编程式导航

router-view

 <router-view></router-view>

路由匹配到的组件将渲染在这里。如果存在子路由,那么在父路由对应的vue文件中也要添加router-view

动态路由匹配

路径参数

const routes = [
  // 动态字段以冒号开始
  { path: '/users/:id', component: User },
]

现在像 /users/johnny 和 /users/jolyne 这样的 URL 都会映射到同一个路由。通过this.$route.params可以获取到这个变量的值。
相同组件实例被复用时,组件的生命周期钩子不会被调用,应该通过监听$route.params 来进行响应。

捕获所有路由或404路由

const routes = [
  // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  // 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
  { path: '/user-:afterUser(.*)', component: UserGeneric },
]

这个也与之前不同,记得以前就一个*

嵌套路由

const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      // 当 /user/:id 匹配成功
      // UserHome 将被渲染到 User 的 <router-view> 内部
      { path: '', component: UserHome },

      // ...其他子路由
    ],
  },
]

嵌套路由需要注意的就是在父级路由对应的vue文件里不要忘记router-view,否则子路由对应的内容不会显示。

编程式导航

相对于通过router-link实现导航,编程式导航的应用更加灵活。
通过router.push() 来实现路由的跳转

// 字符串路径
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' })

注意:如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。

比较常用的还是通过path的形式,实现导航。

replace

作用类似于router.push(),但是导航时不会往history中添加新记录。

go

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

// 向前移动一条记录,与 router.forward() 相同
router.go(1)

// 返回一条记录,与router.back() 相同
router.go(-1)

命名路由

命名路由就是给路由起一个名字,在编程式导航中可以通过name进行路由跳转

重定向

重定向也是通过 routes 配置来完成,下面例子是从 /home 重定向到 /:

const routes = [{ path: '/home', redirect: '/' }]

重定向的目标也可以是一个命名的路由:

const routes = [{ path: '/home', redirect: { name: 'homepage' } }]

动态返回重定向目标

const routes = [
  {
    // /search/screens -> /search?q=screens
    path: '/search/:searchText',
    redirect: to => {
      // 方法接收目标路由作为参数
      // return 重定向的字符串路径/路径对象
      return { path: '/search', query: { q: to.params.searchText } }
    },
  }
]

进阶

导航守卫

正如其名,vue-router 提供的导航守卫主要用来 通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。

全局前置路由

使用router.beforeEach 注册一个全局前置守卫:

const router = createRouter({ ... })

router.beforeEach((to, from) => {
  // ...
  // 返回 false 以取消导航
  return false
})

每个守卫方法接收两个参数:

  • to:即将要进入的目标路由
  • from:当前导航正要离开的路由

可以返回的值如下:

  • false:取消当前的导航
  • 一个路由地址:通过一个路由地址跳转到一个不同的地址

示例:如果路径是 /test 就让他直接跳转到/test/button

router.beforeEach((to, from) => {
  // console.log(to);
  if (to.path == "/test") {
    return "/test/button";
  }
});

除了返回一个路径字符串,其实也可以返回一个对象,可以使用路由的其他参数

if (to.path == "/test") {
  return {
    path: "/test/button",
    query: {
      id: 5,
    },
  };
}

在这里插入图片描述

可选的第三个参数:next

在之前的 Vue Router 版本中,也是可以使用 第三个参数 next 的。它仍然是被支持的,但是官方好像不推荐。容易出错。

全局解析守卫

全局解析路由router.beforeResolve 跟全局前置守卫路由类似。但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用。

官方说法的大体意思就是。路由跳转之前会解析一些数据,在这个过程里你可以执行一些其他操作。

全局后置钩子

全局后置钩子:afterEach 不会改变导航本身,但是对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。

router.afterEach((to, from) => {
  console.log(to, from);
  document.title = "哈哈哈";
});

路由独享守卫

在配置路由时,定义beforeEnter守卫:

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from) => {
      // reject the navigation
      return false
    },
  },
]

beforeEnter 守卫 只在进入路由时触发,不会在 params、query 或 hash 改变时触发。

你也可以将一个函数数组传递给 beforeEnter,这在为不同的路由重用守卫时很有用:

function removeQueryParams(to) {
  if (Object.keys(to.query).length)
    return { path: to.path, query: {}, hash: to.hash }
}

function removeHash(to) {
  if (to.hash) return { path: to.path, query: to.query, hash: '' }
}

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: [removeQueryParams, removeHash],
  },
  {
    path: '/about',
    component: UserDetails,
    beforeEnter: [removeQueryParams],
  },
]

组件内的守卫

你可以在路由组件内直接定义路由导航守卫(传递给路由配置的)

可以为路由组件添加以下配置

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

如果你正在使用组合 API 和 setup 函数来编写组件,你可以通过 onBeforeRouteUpdate 和 onBeforeRouteLeave 分别添加 update 和 leave 守卫。

示例:

setup() {
    onBeforeRouteLeave((to, from) => {
        const answer = window.confirm(
            'Do you really want to leave? you have unsaved changes!'
        )
        // 取消导航并停留在同一页面上
        if (!answer) return false
    })
    
}

在这里插入图片描述

路由元信息

官方文档是这样介绍的:

有时,你可能希望将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的meta属性来实现,并且它可以在路由地址和导航守卫上都被访问到。定义路由的时候你可以这样配置 meta 字段:

const routes = [
  {
    path: '/posts',
    component: PostsLayout,
    children: [
      {
        path: 'new',
        component: PostsNew,
        // 只有经过身份验证的用户才能创建帖子
        meta: { requiresAuth: true }
      },
      {
        path: ':id',
        component: PostsDetail
        // 任何人都可以阅读文章
        meta: { requiresAuth: false }
      }
    ]
  }
]
router.beforeEach((to, from) => {
  // 而不是去检查每条路由记录
  // to.matched.some(record => record.meta.requiresAuth)
  if (to.meta.requiresAuth && !auth.isLoggedIn()) {
    // 此路由需要授权,请检查是否已登录
    // 如果没有,则重定向到登录页面
    return {
      path: '/login',
      // 保存我们所在的位置,以便以后再来
      query: { redirect: to.fullPath },
    }
  }
})

TypeScript

可以通过扩展 RouteMeta 接口来规范输入 meta 字段的格式:

import 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    // 是可选的
    isAdmin?: boolean
    // 每个路由都必须声明
    requiresAuth: boolean
  }
}

组合式API

在 setup 中访问路由和当前路由

在 setup 里面没有访问 this,所以我们不能再直接访问 this. r o u t e r 或 t h i s . router 或 this. routerthis.route。作为替代,我们使用 useRouter 函数:

import { useRouter, useRoute } from 'vue-router'
setup() {
    const router = useRouter()
    const route = useRoute()

    onMounted(() => {
        console.log(router, route)
    })
}

router用来执行一些路由方法,例如:back()go()push() 等方法

route可以用来获取一些属性,例如:queryparamspath 等属性

导航式守卫

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

  setup() {
    // 与 beforeRouteLeave 相同,无法访问 `this`
    onBeforeRouteLeave((to, from) => {
      const answer = window.confirm(
        'Do you really want to leave? you have unsaved changes!'
      )
      // 取消导航并停留在同一页面上
      if (!answer) return false
    })

    const userData = ref()

    // 与 beforeRouteUpdate 相同,无法访问 `this`
    onBeforeRouteUpdate(async (to, from) => {
      //仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
      if (to.params.id !== from.params.id) {
        userData.value = await fetchUser(to.params.id)
      }
    })
  },

过渡动画

全局动画

<router-view v-slot="{ Component }">
  <transition name="fade">
    <component :is="Component" />
  </transition>
</router-view>

单个路由的过渡
如果你想让每个路由的组件有不同的过渡,你可以将元信息和动态的 name 结合在一起,放在 上:

const routes = [
  {
    path: '/custom-transition',
    component: PanelLeft,
    meta: { transition: 'slide-left' },
  },
  {
    path: '/other-transition',
    component: PanelRight,
    meta: { transition: 'slide-right' },
  },
]
<router-view v-slot="{ Component, route }">
  <!-- 使用任何自定义过渡和回退到 `fade` -->
  <transition :name="route.meta.transition || 'fade'">
    <component :is="Component" />
  </transition>
</router-view>

基于路由的动态过渡

官方提供的这个例子就很有意思

<!-- 使用动态过渡名称 -->
<router-view v-slot="{ Component, route }">
  <transition :name="route.meta.transition">
    <component :is="Component" />
  </transition>
</router-view>

我们可以添加一个 全局后置钩子,根据路径的深度动态添加信息到 meta 字段。

router.afterEach((to, from) => {
  const toDepth = to.path.split('/').length
  const fromDepth = from.path.split('/').length
  to.meta.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})

滚动行为

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

注意: 这个功能只在支持 history.pushState 的浏览器中可用。

当创建一个 Router 实例,你可以提供一个 scrollBehavior 方法:

const router = createRouter({
  history: createWebHashHistory(),
  routes: [...],
  scrollBehavior(to, from, savedPosition) {
    // 始终滚动到顶部
    return { top: 0 }
  },
})

返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:

const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  },
})

延迟滚动

const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ left: 0, top: 0 })
      }, 500)
    })
  },
})

路由懒加载

// 将
// import UserDetails from './views/UserDetails'
// 替换成
const UserDetails = () => import('./views/UserDetails')
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无知的小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值