Skip to content

Vue3 实战 - 配置路由

安装

shell
$ pnpm add vue-router

基本使用

定义路由

新建router/routes.ts

ts
 const routes = [
  {
    path: "/index",
    component: () => import("@/views/index.vue"), //路由懒加载
  },
  {
    path: "/home",
    component: () => import("@/views/home.vue"),
  },
  // Resolve refresh page, route warnings  
  {  
    path: "/:pathMatch(.*)*",  
    component: () => import("@/components/ErrorMessage/404.vue"),  
  }, 
 ];
 
 export default routes;

创建路由实例

新建router/index.ts

ts
 import { createRouter, createWebHistory } from "vue-router";
 import routes from "./routes";
 
 const router = createRouter({
   history: createWebHistory(), //可传参数,配置base路径,例如'/app'
   routes,
 });
 
 export default router;

注册路由

修改main.ts

ts
 import router from "./router/index";
 
 const app = createApp(App);
 
 app.use(router); //注册路由

定义路由出口

修改 App.vue

vue
<template>
 <router-view> App </router-view>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
 transition: all 0.2s ease;
}

.fade-enter-from,
.fade-leave-active {
 opacity: 0;
}
</style>

router-view 将显示与 url 对应的组件,可以把它放在任何地方以适应布局。Transition 是基于路由的动态过渡动效

嵌套路由

在 App.vue 中定义的 router-view,这是顶层的出口,渲染最高级路由匹配到的组件。

如果要实现登录之后左侧菜单栏不变,右侧随路由的切换变化显示的内容,需使用嵌套路由。

定义路由

修改router/routes.ts

ts
import { RouteRecordRaw } from "vue-router";

/**
 * 动态子路由
 */
export const dynamicRoutes: RouteRecordRaw[] = [
	{
		path: "/slide",
		name: "slide",
		component: () => import("@/views/slide/index.vue"),
		meta: {
			title: "演示文稿",
			icon: "slide",
		},
	},
];

/**
 * staticRouter: 静态路由
 */
export const staticRoutes: RouteRecordRaw[] = [
	{
		path: "/login",
		name: "login",
		component: () => import("@/views/login/index.vue"),
		meta: {
			title: "登录",
		},
	},
	{
		path: "/",
		name: "layout",
		component: () => import("@/layout/index.vue"),
		children: [...dynamicRoutes],
	},
];

/**
 * errorRouter: 错误页面路由
 */
export const errorRoutes: RouteRecordRaw[] = [
	{
		path: "/403",
		name: "403",
		component: () => import("@/components/ErrorMessage/403.vue"),
		meta: {
			title: "403",
		},
	},
	{
		path: "/404",
		name: "404",
		component: () => import("@/components/ErrorMessage/404.vue"),
		meta: {
			title: "404",
		},
	},
	{
		path: "/500",
		name: "500",
		component: () => import("@/components/ErrorMessage/500.vue"),
		meta: {
			title: "500",
		},
	},
	// Resolve refresh page, route warnings
	{
		path: "/:pathMatch(.*)*",
		component: () => import("@/components/ErrorMessage/404.vue"),
	},
];

路由实例

bash
import { createRouter, createWebHistory } from 'vue-router'
import { errorRoutes, staticRoutes } from './routes'

const router = createRouter({
    history: createWebHistory(),
    routes: [...staticRoutes, ...errorRoutes]
})

export default router

重定向

情景:在嵌套路由中,当访问/home时想重定向到/home/user

修改router/routes.ts

ts
{
    path: '/home',
    component: () => import('@/pages/home.vue'),
    redirect: '/home/user', //新增
    children: [
      {
        path: '/home/user',
        component: () => import('@/pages/user.vue'),
      },
      {
        path: '/home/manage',
        component: () => import('@/pages/manage.vue'),
      },
    ],
  },

声明式、编程式导航

声明式导航

vue
<router-link to="/home"> 跳转home </router-link>;

编程式导航

ts
<script setup lang="ts">
import { useRouter } from 'vue-router';

const router = useRouter();

const handleManage = () => {
  router.push('/home/manage');
};
</script>

路由参数

query 传参

vue
//页面传参
<script setup lang="ts">
import { useRouter } from 'vue-router';

const router = useRouter();

const handleManage = () => {
  router.push({
    path: '/home/manage',
    query: {
      plan: '123',
    },
  });
};
</script>

//页面接参
<script setup lang="ts">
import { useRoute } from 'vue-router';
const route = useRoute();

console.log(route.query.plan); //query接参
</script>

动态路由匹配

ts
//定义路由
{
    path: '/register/:plan', // 动态字段以冒号开始
    component: () => import('@/pages/register.vue'),
  },

//页面传参
<script setup lang="ts">
import { useRouter } from 'vue-router';
const router = useRouter();

const handleManage = () => {
  router.push('/register/123');
};
</script>

//页面接参
<script setup lang="ts">
import { useRoute } from 'vue-router';
const route = useRoute();

console.log(route.params.plan); //params接参
</script>

导航守卫

全局前置守卫

使用场景:做登录判断,未登陆用户跳转到登录页修改router/index.ts

router.beforeEach((to, from) => {
    if (to.path === '/index') {
      //在登录页做清除操作,如清除token等
    }

    if (!localStorage.getItem('token') && to.path !== '/index') {
      // 未登陆且访问的不是登录页,重定向到登录页面
      return '/index';
    }
  });

路由独享守卫

使用场景:部分页面不需要登录,部分页面需要登录才能访问

修改router/routes.ts

const auth = () => {
  if (!localStorage.getItem("token")) {
    // 未登陆,重定向到登录页面
    return "/index";
  }
};

const routes = [
  ...{
    path: "/home",
    component: () => import("@/pages/home.vue"),
    redirect: "/home/user",
    children: [
      {
        path: "/home/user",
        component: () => import("@/pages/user.vue"),
      },
      {
        path: "/home/manage",
        component: () => import("@/pages/manage.vue"),
        beforeEnter: auth, //路由独享守卫
      },
    ],
  },
];

组件内守卫

使用情景:预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消

<script setup lang="ts">
import { onBeforeRouteLeave } from 'vue-router';

// 与 beforeRouteLeave 相同,无法访问 `this`
onBeforeRouteLeave((to, from) => {
  const answer = window.confirm('确定离开吗');
  // 取消导航并停留在同一页面上
  if (!answer) return false;
});
</script>

路由元信息

将自定义信息附加到路由上,例如页面标题,是否需要权限,是否开启页面缓存等

使用情景:使用路由元信息+全局前置守卫实现部分页面不需要登录,部分页面需要登录才能访问

修改router/routes.ts

const routes = [
  ...{
    path: "/home",
    component: () => import("@/pages/home.vue"),
    redirect: "/home/user",
    children: [
      {
        path: "/home/user",
        component: () => import("@/pages/user.vue"),
      },
      {
        path: "/home/manage",
        component: () => import("@/pages/manage.vue"),
        meta: {
          title: "管理页", // 页面标题
          auth: true, //需要登录权限
        },
      },
    ],
  },
];

修改router/index.ts

router.beforeEach((to, from) => {
  if (!localStorage.getItem("token") && to.meta.auth) {
    // 此路由需要授权,请检查是否已登录
    // 如果没有,则重定向到登录页面
    return {
      path: "/index",
      // 保存我们所在的位置,以便以后再来
      query: { redirect: to.fullPath },
    };
  }
});