vue3全家桶 之 Vue Router4的使用

13,602 阅读3分钟

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。随着vue3发布,Vue Router也更新到了的4.x版本来与vue3的新特性做适配。本文基于Vue Router4。

Vue Router

1. 安装及配置

执行 npm install vue-router@4 。执行命令安装完成之后,在目录下创建 src/router/index.js 写入下面的配置:

import { createRouter, createWebHashHistory } from 'vue-router'
import Layout from '@/layout/index.vue'
import Home from '@/views/home/Home.vue'
const routes = [
  {
    path: '/',
    component: Layout,
    children: [{ path: '', component: Home }],
  },
]

export default createRouter({
history: createWebHashHistory(),
routes,
})

main.js中使用

// ...+
import router from './router/index'
createApp(App).use(router).mount('#app')

tips : hash模式使用 createWebHashHistory方法, history模式使用 createWebHistory方法。

2. 动态路由匹配

  • 路由配置
{ path: '/courseinfo/:title', component: CourseInfo }
  • 两种跳转方式
  <router-link :to="{ path: '/courseinfo/c' }">
      跳转到c语言详情页
  </router-link>
import { useRouter } from 'vue-router';

const router = useRouter();
function toInfo() {
  router.push({ path: '/courseinfo/python' })
}
  • 获取参数的方式 $route.params.title 只能在模板中使用 或 useRoute().params.title必须在setup中调用。因为 setup 中不能使用this,所以 $route 只能在模板中使用。
  • 响应参数变化: 使用带有参数的路由时需要注意的是,当用户从 /courseinfo/javascript 导航到 /courseinfo/python 时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用。解决方法:watch $route 对象上的任意属性,在这个场景中,就是 $route.params;或者使用导航守卫。
  • 捕获所有路由或 404 Not found 路由
const routes = [
  // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  // 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
  { path: '/user-:afterUser(.*)', component: UserGeneric },
]

3. 编程式导航

声明式编程式
<router-link :to="...">router.push(...)

常见用法

// 传递path
router.push(`/user/${username}`)
// 或者
router.push({ path: `/user/${username}` })

命名路由

路由定义时需要⼀个name,跳转时可根据name值跳转,router/index.js

{ path: '/courseinfo/:title', component: CourseInfo, name: 'CourseInfo' },

导航时传参

params传参

// 使⽤`name`和`params`搭配,以利⽤⾃动的URL编码;不能使⽤`path`和`params`搭配
router.push({ name: 'user', params: { username } }) 

query传参,跳转到user页,链接是 /user?data=123,获取query参数 $route.query.data

router.push({ name: 'user', query: { data:123 } }) 

router.replace方法

跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

声明式编程式
<router-link :to="..." replace>router.replace(...)

router.go(n)方法

router.go(-1)  // 返回、后退一步

4. 路由守卫

全局守卫

  • 范围最⼤,任何路由导航都会触发回调
const router = createRouter({ ... })

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

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

  • 可选的第三个参数 next 示例:使用路由守卫进行登录权限验证,未登录跳转登录页,已登录跳转登录成功页。src/permission.js:
router.beforeEach((to, from, next) => {
  if (to.path === '/loginsuccess') {
    if (localStorage.getItem('isLogin')) {
      next();
    } else {
      next({ path: '/login' });
    }
  } else {
    next();
  }
});

根据localStorage缓存判断,可以手动清除切换状态调试观察效果。

路由独享的守卫

你可以直接在路由配置上定义 beforeEnter 守卫:

  // src/router/index.js
  {
    path: '/loginsuccess',
    name: 'LoginSuccess',
    component: LoginSuccess,
    beforeEnter: (to, from) => {
      console.log("----登录成功页面的路由独享的守卫")
    }
  },

beforeEnter 守卫 只在进入路由时触发,不会在 paramsquery 或 hash 改变时触发。例如,从 /users/2 进入到 /users/3 或者从 /users/2#info 进入到 /users/2#projects。它们只有在 从一个不同的 路由导航时,才会被触发。

组件内路由守卫

  • 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`
  },
}

setup中使用时,可以通过 onBeforeRouteUpdate 和 onBeforeRouteLeave 分别添加 update 和 leave 守卫。提示:setup中没有提供beforeRouteEnter的等效方法。

5. 路由元信息

定义路由配置时通过添加meta字段来附加额外信息。src/permission.js 改写为 使用meta路由元信息判断跳转登录成功页还是去登陆

router.beforeEach((to, from, next) => {
  if (to.meta.auth) {
    if (localStorage.getItem('isLogin')) {
      next();
    } else {
      next({ path: '/login' });
    }
  } else {
    next();
  }
});

6. 路由懒加载

打包时将单个路由组件分⽚打包,访问时才异步加载,可以有效降低app尺⼨和加载时间。 src/router/index.js

const Login = () => import('@/views/login/Login.vue');

const routes = [
    { path: '/login', name: 'Login', component: Login }
]

7. composition api中使用

在组合式Api和setup中无法访问this,无法使用this.$router等。所以新增了一些新的函数来访问router route导航钩子等。

  • useRouter和useRoute
<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()
//...
</script>
  • 路由守卫钩子
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

// 等效于beforeRouteLeave,但是不能访问`this`
onBeforeRouteLeave((to, from) => {})

// 等效于beforeRouteUpdate,但是不能访问`this`
onBeforeRouteUpdate((to, from) => {})

</script>

8. 动态路由

有时候需要在程序运行时添加或删除一些路由,这时,就可以用下面的方法。

  • 新增路由
const TestRoute = () => import('@/views/vuerouter/TestRoute.vue');

  router.addRoute({
    path: '/testroute',
    component: TestRoute,
    name: 'TestRoute',
  });
  • 移除路由
// 第一种方式:通过 name 删除
router.removeRoute('TestRoute');
// 第二种方式:名字重复时自动覆盖
router.addRoute({ path: '/about', name: 'about', component: About })
// 这将会删除之前已经添加的路由,因为他们具有相同的名字且名字必须是唯一的
router.addRoute({ path: '/other', name: 'about', component: Other })
  • 添加嵌套路由,通过参数1传递⽗路由name即可
router.addRoute('parentRouteName', {...})

9. 缓存和过渡动画

缓存

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

来回切换页面想要保存组件状态时,可以使用<keep-alive>结合v-slotApi包裹动态组件,这时组件实例就会被缓存。

过渡动画

  • 基本使用 src/layout/AppMain.vue
    <router-view v-slot="{ Component }">
      <transition name="fade" mode="out-in">
        <component :is="Component" />
      </transition>
    </router-view>

Transition 的所有功能 在这里同样适用,下面定义css动画

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
  • 单个路由动画 添加meta路由元信息,通过meta设置需要特殊过渡动画
{ path: '', component: VuerouterPage, meta: { transition: 'slide' } },

AppMain.vue

    <router-view v-slot="{ Component, route }">
      <transition :name="route.meta.transition || 'fade'" mode="out-in">
        <component :is="Component" />
      </transition>
    </router-view>
/* slide transition 左进右出效果 */
.slide-enter-active,
.slide-leave-active {
  transition: 0.5s ease;
}

.slide-enter-from {
  transform: translateX(-50px);
  opacity: 0;
}
.slide-leave-to {
  transform: translateX(50px);
  opacity: 0;
}
  • 根据路由之间关系动态决定动画类型
<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.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})

本文demo地址 github.com/kongcodes/v…

vue3相关内容