Vue Router

什么是 Vue Router?

Vue Router 是 Vue.js 官方的路由管理器,用于构建单页应用(SPA)。它与 Vue.js 核心深度集成,让构建单页应用变得更加简单。

1. 安装 Vue Router

使用 npm 或 yarn 安装 Vue Router。

示例:安装 Vue Router

# 使用 npm
npm install vue-router@4

# 使用 yarn
yarn add vue-router@4

2. 基本配置

创建路由配置文件,并在应用中使用。

示例:路由配置

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

// 导入组件
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'
import ContactView from '../views/ContactView.vue'

// 定义路由
const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    component: AboutView
  },
  {
    path: '/contact',
    name: 'contact',
    component: ContactView
  }
]

// 创建路由实例
const router = createRouter({
  // 路由模式:createWebHistory (HTML5 History API) 或 createWebHashHistory (hash 模式)
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

export default router

在应用中使用路由

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

// 使用路由
app.use(router)

app.mount('#app')

App.vue 中添加路由出口

<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link> |
      <router-link to="/about">关于我们</router-link> |
      <router-link to="/contact">联系我们</router-link>
    </nav>
    
    <!-- 路由出口:匹配的组件将渲染在这里 -->
    <router-view />
  </div>
</template>

3. 路由导航

Vue Router 提供了多种导航方式:

3.1 声明式导航

使用 <router-link> 组件进行导航。

示例:声明式导航

<!-- 基本导航 -->
<router-link to="/">首页</router-link>

<!-- 命名路由 -->
<router-link :to="{ name: 'about' }">关于我们</router-link>

<!-- 带参数的路由 -->
<router-link :to="{ name: 'user', params: { id: 123 } }">用户 123</router-link>

<!-- 带查询参数 -->
<router-link :to="{ path: '/search', query: { q: 'vue' } }">搜索 Vue</router-link>

<!-- 替换当前历史记录 -->
<router-link to="/" replace>首页</router-link>

<!-- 激活状态样式 -->
<router-link to="/" active-class="active">首页</router-link>

3.2 编程式导航

使用 router.push() 或 router.replace() 进行导航。

示例:编程式导航

<template>
  <div>
    <button @click="goToHome">返回首页</button>
    <button @click="goToAbout">前往关于页</button>
    <button @click="goToUser">查看用户</button>
    <button @click="goBack">返回上一页</button>
  </div>
</template>

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

const router = useRouter();
const route = useRoute();

// 前往首页
function goToHome() {
  router.push('/');
}

// 前往关于页
function goToAbout() {
  router.push({ name: 'about' });
}

// 查看用户
function goToUser() {
  router.push({ name: 'user', params: { id: 123 } });
}

// 返回上一页
function goBack() {
  router.back();
  // 或 router.go(-1);
}
</script>

4. 动态路由

使用动态路由参数匹配不同的路由。

示例:动态路由

// router/index.js
const routes = [
  {
    path: '/user/:id',
    name: 'user',
    component: () => import('../views/UserView.vue')
  },
  {
    path: '/post/:year/:month/:day',
    name: 'post',
    component: () => import('../views/PostView.vue')
  }
]

在组件中获取路由参数

<template>
  <div class="user">
    <h2>用户详情</h2>
    <p>用户 ID: {{ route.params.id }}</p>
    <p>查询参数: {{ route.query }}</p>
    <p>路由名称: {{ route.name }}</p>
    <p>完整路径: {{ route.fullPath }}</p>
  </div>
</template>

<script setup>
import { useRoute } from 'vue-router';

const route = useRoute();

// 监听路由参数变化
import { watch } from 'vue';
watch(() => route.params.id, (newId, oldId) => {
  console.log(`用户 ID 从 ${oldId} 变为 ${newId}`);
  // 可以在这里重新获取数据
});
</script>

5. 嵌套路由

使用嵌套路由实现复杂的页面结构。

示例:嵌套路由

// router/index.js
const routes = [
  {
    path: '/dashboard',
    name: 'dashboard',
    component: () => import('../views/DashboardView.vue'),
    children: [
      {
        path: '', // 默认子路由
        name: 'dashboard-home',
        component: () => import('../views/DashboardHomeView.vue')
      },
      {
        path: 'profile',
        name: 'dashboard-profile',
        component: () => import('../views/DashboardProfileView.vue')
      },
      {
        path: 'settings',
        name: 'dashboard-settings',
        component: () => import('../views/DashboardSettingsView.vue')
      }
    ]
  }
]

父组件中添加子路由出口

<template>
  <div class="dashboard">
    <h2>仪表盘</h2>
    <nav>
      <router-link to="/dashboard">首页</router-link>
      <router-link to="/dashboard/profile">个人资料</router-link>
      <router-link to="/dashboard/settings">设置</router-link>
    </nav>
    <!-- 子路由出口 -->
    <router-view />
  </div>
</template>

6. 路由守卫

路由守卫用于控制路由的访问权限,包括全局守卫、路由独享守卫和组件内守卫。

示例:全局守卫

// router/index.js

// 全局前置守卫
router.beforeEach((to, from, next) => {
  console.log('全局前置守卫', to, from);
  
  // 检查用户是否登录
  const isLoggedIn = localStorage.getItem('isLoggedIn');
  
  // 如果访问的是需要登录的页面,且用户未登录,则重定向到登录页
  if (to.meta.requiresAuth && !isLoggedIn) {
    next({ name: 'login' });
  } else {
    next(); // 继续导航
  }
});

// 全局解析守卫
router.beforeResolve((to, from, next) => {
  console.log('全局解析守卫', to, from);
  next();
});

// 全局后置钩子
router.afterEach((to, from) => {
  console.log('全局后置钩子', to, from);
  // 可以在这里设置页面标题
  document.title = to.meta.title || 'Vue3 App';
});

示例:路由独享守卫

const routes = [
  {
    path: '/admin',
    name: 'admin',
    component: () => import('../views/AdminView.vue'),
    meta: {
      requiresAuth: true,
      requiresAdmin: true
    },
    // 路由独享守卫
    beforeEnter: (to, from, next) => {
      console.log('路由独享守卫', to, from);
      const userRole = localStorage.getItem('userRole');
      if (to.meta.requiresAdmin && userRole !== 'admin') {
        next({ name: 'home' });
      } else {
        next();
      }
    }
  }
]

示例:组件内守卫

<script setup>
import { onBeforeRouteEnter, onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';

// 进入路由前
onBeforeRouteEnter((to, from, next) => {
  console.log('进入路由前', to, from);
  next();
});

// 路由更新前(参数变化时)
onBeforeRouteUpdate((to, from, next) => {
  console.log('路由更新前', to, from);
  next();
});

// 离开路由前
onBeforeRouteLeave((to, from, next) => {
  console.log('离开路由前', to, from);
  // 可以在这里询问用户是否确认离开
  if (confirm('确定要离开吗?')) {
    next();
  } else {
    next(false); // 取消导航
  }
});
</script>

7. 路由懒加载

使用路由懒加载可以提高应用的初始加载性能,只在需要时才加载对应的组件。

示例:路由懒加载

// router/index.js
const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/HomeView.vue')
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue')
  },
  {
    path: '/contact',
    name: 'contact',
    component: () => import('../views/ContactView.vue')
  }
]

8. 路由元信息

使用路由元信息可以为路由添加额外的信息,如页面标题、权限要求等。

示例:路由元信息

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView,
    meta: {
      title: '首页',
      requiresAuth: false
    }
  },
  {
    path: '/about',
    name: 'about',
    component: AboutView,
    meta: {
      title: '关于我们',
      requiresAuth: false
    }
  },
  {
    path: '/dashboard',
    name: 'dashboard',
    component: DashboardView,
    meta: {
      title: '仪表盘',
      requiresAuth: true
    }
  }
]

9. Vue Router 最佳实践

  • 使用路由懒加载提高初始加载性能
  • 使用命名路由而不是硬编码路径
  • 合理使用路由守卫控制访问权限
  • 为路由添加元信息,便于统一管理
  • 使用嵌套路由组织复杂的页面结构
  • 考虑使用路由过渡动画提升用户体验
  • 注意路由参数的类型转换和验证
  • 避免在路由守卫中执行复杂的异步操作