Vue.js: Vue Router 路由

最后更新:2026-08-26

Vue Router 是 Vue 官方的路由管理器——让你的 SPA(单页应用)有多个"页面",每个 URL 对应一个组件。Vue Router 4 是 Vue 3 配套版本,完全支持 Composition API。

SPA 路由的核心是URL ↔ 组件的映射。掌握 Vue Router 4 是开发任何非 trivial Vue 应用的必备技能。

1. 你将学到


2. 一个 SPA 的"404 噩梦"

(1) 痛点:5 个页面,5 份重复代码

Alice 的后台有 5 个页面(Home/Products/Orders/Users/Settings):

VUE
<!-- ❌ 翻车版:用 v-if 切换 5 个组件 -->
<template>
  <div>
    <button @click="currentPage = 'home'">Home</button>
    <button @click="currentPage = 'products'">Products</button>
    <button @click="currentPage = 'orders'">Orders</button>
    
    <div v-if="currentPage === 'home'"><HomePage /></div>
    <div v-else-if="currentPage === 'products'"><ProductsPage /></div>
    <div v-else-if="currentPage === 'orders'"><OrdersPage /></div>
    <div v-else>Page not found</div>
  </div>
</template>

4 个问题:

(2) Vue Router 4 解法:5 行路由 + 5 个组件

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

const routes = [
  { path: '/', component: () => import('@/views/HomePage.vue') },
  { path: '/products', component: () => import('@/views/ProductsPage.vue') },
  { path: '/orders', component: () => import('@/views/OrdersPage.vue') },
  { path: '/users', component: () => import('@/views/UsersPage.vue') },
  { path: '/settings', component: () => import('@/views/SettingsPage.vue') }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router
VUE
<!-- App.vue -->
<template>
  <nav>
    <router-link to="/">Home</router-link>
    <router-link to="/products">Products</router-link>
    <router-link to="/orders">Orders</router-link>
  </nav>
  
  <!-- ✅ 关键:router-view 渲染当前路由的组件 -->
  <router-view />
</template>

5 行路由 + 1 个 router-view = 完整 SPA。URL 自动同步,浏览器前进/后退工作。

(3) 收益

使用 Vue Router 后:


3. 路由初始化

(1) 完整初始化代码

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

// 1. 定义路由(懒加载)
const routes = [
  { path: '/', component: () => import('@/views/HomePage.vue') },
  { path: '/products', component: () => import('@/views/ProductsPage.vue') }
]

// 2. 创建 router 实例
const router = createRouter({
  // history 模式:URL 无 #(推荐)
  history: createWebHistory(),
  // hash 模式:URL 带 #(兼容老浏览器)
  // history: createWebHashHistory(),
  
  routes,
  
  // 滚动行为:路由切换时滚到顶部
  scrollBehavior(to, from, savedPosition) {
    return savedPosition || { top: 0 }
  }
})

export default router

(2) main.js 注册

JS
// src/main.js
const { createApp } = Vue
import App from './App.vue'
import router from './router'

const app = createApp(App)
app.use(router)  // 注册路由
app.mount('#app')

(3) 2 大 history 模式对比

维度 createWebHistory createWebHashHistory
URL 形式 /products /#/products
SEO ✅ 友好 ❌ 不友好
服务器配置 需要(try_files) 不需要
浏览器兼容 IE10+ IE8+
推荐度 ⭐⭐⭐⭐⭐ ⭐⭐

4. 5 种路由配置

(1) 静态路由

JS
const routes = [
  { path: '/', component: () => import('@/views/HomePage.vue') },
  { path: '/about', component: () => import('@/views/AboutPage.vue') },
  { path: '/contact', component: () => import('@/views/ContactPage.vue') }
]

(2) 动态路由(参数)

JS
const routes = [
  // /products/123
  { 
    path: '/products/:id', 
    component: () => import('@/views/ProductDetailPage.vue') 
  },
  // /users/456/posts/789
  { 
    path: '/users/:userId/posts/:postId', 
    component: () => import('@/views/PostPage.vue') 
  },
  // 可选参数
  { 
    path: '/news/:category?', 
    component: () => import('@/views/NewsPage.vue') 
  }
]
VUE
<!-- ProductDetailPage.vue 接收参数 -->
<script setup>
import { useRoute } from 'vue-router'

const route = useRoute()
console.log(route.params.id)  // '123'
</script>

(3) 嵌套路由

JS
const routes = [
  {
    path: '/users',
    component: () => import('@/views/UsersPage.vue'),
    children: [
      {
        path: '',  // 默认子路由
        component: () => import('@/views/UsersListPage.vue')
      },
      {
        path: ':id',  // /users/123
        component: () => import('@/views/UserDetailPage.vue')
      }
    ]
  }
]
VUE
<!-- UsersPage.vue -->
<template>
  <div>
    <h1>Users</h1>
    <router-view />  <!-- 子路由出口 -->
  </div>
</template>

(4) 命名路由

JS
const routes = [
  { 
    path: '/products/:id', 
    name: 'product-detail',  // 命名路由
    component: () => import('@/views/ProductDetailPage.vue') 
  }
]
VUE
<!-- 通过 name 跳转,避免硬编码 URL -->
<router-link :to="{ name: 'product-detail', params: { id: 123 } }">
  Product 123
</router-link>

(5) 通配符 404

JS
const routes = [
  { path: '/', component: () => import('@/views/HomePage.vue') },
  // ... 其他路由
  {
    path: '/:pathMatch(.*)*',  // 匹配所有未定义的路由
    component: () => import('@/views/NotFoundPage.vue')
  }
]

5. 5 种导航方式

VUE
<template>
  <!-- 字符串路径 -->
  <router-link to="/products">Products</router-link>
  
  <!-- 命名路由(推荐) -->
  <router-link :to="{ name: 'products' }">Products</router-link>
  
  <!-- 动态参数 -->
  <router-link :to="{ name: 'product-detail', params: { id: 123 } }">
    Product 123
  </router-link>
  
  <!-- Query 参数 -->
  <router-link :to="{ path: '/search', query: { q: 'vue' } }">
    Search
  </router-link>
</template>

(2) 编程式导航(router.push)

JS
import { useRouter } from 'vue-router'

const router = useRouter()

function goToProduct(id) {
  router.push(`/products/${id}`)  // 字符串
  // 或
  router.push({ name: 'product-detail', params: { id } })  // 对象
}

function goBack() {
  router.back()  // 后退
}

function goForward() {
  router.forward()  // 前进
}

function replaceCurrent() {
  router.replace('/login')  // 替换(不留历史)
}

(3) 5 种 push vs replace 区别

方式 行为 历史记录
router.push() 跳转到新页面 留下记录(可后退)
router.replace() 替换当前页面 不留记录(不可后退)
router.go(n) 前进/后退 n 步 走历史
router.back() 后退一步 走历史
router.forward() 前进一步 走历史

(4) 编程式 vs 声明式对比

场景 推荐
用户点击链接 router-link(声明式)
JS 触发跳转 router.push(编程式)
表单提交后跳转 router.push
登录后跳转 router.replace
404 自动回退 router.back

6. 路由守卫(导航守卫)

(1) 3 种守卫类型

JS
// 1. 全局前置守卫(最常用)
router.beforeEach((to, from, next) => {
  const isLoggedIn = !!localStorage.getItem('token')
  
  if (to.meta.requiresAuth && !isLoggedIn) {
    next('/login')  // 跳登录
  } else {
    next()  // 放行
  }
})

// 2. 路由独享守卫
const routes = [
  {
    path: '/admin',
    component: () => import('@/views/AdminPage.vue'),
    beforeEnter: (to, from, next) => {
      if (isAdmin()) next()
      else next('/403')
    }
  }
]

// 3. 组件内守卫
// 组件内:beforeRouteEnter / beforeRouteUpdate / beforeRouteLeave

(2) 完整登录守卫示例

JS
// router/index.js
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')
  const isLoggedIn = !!token
  
  // 需要登录的页面
  if (to.meta.requiresAuth && !isLoggedIn) {
    return next({ 
      name: 'login', 
      query: { redirect: to.fullPath }  // 登录后跳回
    })
  }
  
  // 已登录访问登录页
  if (to.name === 'login' && isLoggedIn) {
    return next({ name: 'home' })
  }
  
  next()
})
JS
// 路由 meta 配置
const routes = [
  {
    path: '/dashboard',
    component: () => import('@/views/DashboardPage.vue'),
    meta: { requiresAuth: true }  // 需要登录
  }
]

7. 完整示例:电商后台路由系统

▶ 示例:手写 Hash 路由器(CDN 模拟 SPA 路由)

HTML 📖 仅展示
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

<style>
a { padding: 6px 12px; margin: 2px; text-decoration: none; color: #333; }
a.active { background: #42b883; color: white; border-radius: 4px; }
.page { padding: 1rem; border: 1px solid #ddd; margin-top: 0.5rem; min-height: 80px; }
</style>

<div id="app">
  <nav>
    <a href="#/" :class="{ active: route === '/' }">Home</a>
    <a href="#/about" :class="{ active: route === '/about' }">About</a>
    <a href="#/user/123" :class="{ active: route === '/user/123' }">User 123</a>
    <a href="#/products" :class="{ active: route === '/products' }">Products</a>
  </nav>

  <div class="page">
    <component :is="currentPage"></component>
  </div>

  <p style="color: #999; font-size: 0.85rem;">
    当前路由: {{ route }} | 历史栈: {{ history.length }} 步
  </p>
  <button @click="goBack" :disabled="history.length <= 1">← 后退</button>
  <button @click="goForward" :disabled="!canForward">前进 →</button>
</div>

<script>
const { createApp, ref, computed, onMounted, onUnmounted } = Vue

// 5 个页面组件
const Home = { template: '<h3>🏠 Home</h3><p>这是首页</p>' }
const About = { template: '<h3>ℹ️ About</h3><p>关于我们</p>' }
const User = {
  props: ['id'],
  template: '<h3>👤 User {{ id }}</h3><p>用户资料页</p>'
}
const Products = {
  template: '<h3>📦 Products</h3><p>商品列表页(动态参数示例:<code>useRoute().params.id</code>)</p>'
}
const NotFound = { template: '<h3>❌ 404 Not Found</h3>' }

// 路由表
const routes = {
  '/': Home,
  '/about': About,
  '/user/123': User,
  '/products': Products
}

const App = {
  components: { Home, About, User, Products, NotFound },
  setup() {
    const route = ref(window.location.hash.slice(1) || '/')
    const history = ref([route.value])
    const forwardStack = ref([])

    function updateRoute() {
      const newRoute = window.location.hash.slice(1) || '/'
      if (newRoute !== route.value) {
        history.value.push(newRoute)
        forwardStack.value = []
        route.value = newRoute
      }
    }

    onMounted(() => window.addEventListener('hashchange', updateRoute))
    onUnmounted(() => window.removeEventListener('hashchange', updateRoute))

    function goBack() {
      if (history.value.length > 1) {
        const last = history.value.pop()
        forwardStack.value.push(last)
        const prev = history.value[history.value.length - 1]
        window.location.hash = prev
      }
    }

    function goForward() {
      if (forwardStack.value.length > 0) {
        const next = forwardStack.value.pop()
        history.value.push(next)
        window.location.hash = next
      }
    }

    // 解析动态参数
    const currentPage = computed(() => {
      if (routes[route.value]) {
        // 动态路由:/user/123 匹配 User 组件
        if (route.value.startsWith('/user/')) {
          return { ...User, props: { id: route.value.split('/')[2] } }
        }
        return routes[route.value]
      }
      return NotFound
    })

    const canForward = computed(() => forwardStack.value.length > 0)

    return { route, history, currentPage, goBack, goForward, canForward }
  }
}

createApp(App).mount('#app')
</script>
逻辑代码 86 行(超过 40 行限制,仅展示)
⚠️ 此为教学演示 Hash 路由原理。生产项目用 vue-router 官方库(必须 Vite)。

▶ 示例:5 种导航方式速查

方式 代码 适用
声明式链接 <router-link to="/products"> 用户点击链接
字符串路径 router.push('/products') JS 跳转
命名路由 router.push({ name: 'products' }) 推荐(重命名不破坏)
动态参数 router.push({ name: 'user', params: { id: 123 } }) 详情页
Query 参数 router.push({ path: '/search', query: { q: 'vue' } }) 搜索/过滤

▶ 示例:完整登录守卫(⚠️ 需 Vite 项目)

JS
// router/index.js
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')
  const isLoggedIn = !!token

  if (to.meta.requiresAuth && !isLoggedIn) {
    return next({ name: 'login', query: { redirect: to.fullPath } })
  }

  if (to.name === 'login' && isLoggedIn) {
    return next({ name: 'home' })
  }

  next()
})

// 路由 meta 配置
const routes = [
  {
    path: '/dashboard',
    component: () => import('@/views/DashboardPage.vue'),
    meta: { requiresAuth: true }  // 需要登录
  }
]
▶ 试一试

▶ 示例:5 大常见错误速查

错误 现象 解决
router-view 缺失 页面不显示 添加 <router-view />
路由没懒加载 首屏慢 () => import() 动态导入
守卫死循环 跳不出 避免 next() 又触发 beforeEach
嵌套路由无 outlet 子页面不显示 父组件加 <router-view />
404 路由缺失 报错 path: '/:pathMatch(.*)*'

▶ 示例:5 大性能对比

模式 首屏 切换 适用
静态 import 小项目
动态 import ⭐⭐⭐⭐⭐ 首次慢 推荐
路由分组懒加载 ⭐⭐⭐⭐⭐ 首次慢 大项目
prefetch ⭐⭐⭐⭐ 用户体验优先
SSR ⭐⭐⭐⭐⭐ Nuxt 3

❓ 常见问题

Q createWebHistory 和 createWebHashHistory 选哪个?
A createWebHistory(URL 无 #,SEO 友好)需要服务器配置;createWebHashHistory(URL 带 #)兼容老浏览器。生产环境推荐 createWebHistory。
Q 动态路由怎么接收参数?
AuseRoute() 钩子:const route = useRoute(); route.params.id
Q router.push 和 router.replace 区别?
A push 留历史记录(可后退),replace 替换当前(不留历史)。登录后用 replace(不能后退回登录页)。
Q 路由守卫能异步吗?
A 能。beforeEach(async (to, from, next) => { await checkAuth(); next() })。但要小心避免死循环。
Q 嵌套路由怎么实现?
A 父路由用 children: [] 配置子路由,父组件模板中加 <router-view /> 作为子路由出口。
Q Vue Router 4 和 Vue Router 3 区别?
A Vue Router 4 支持 Composition API(useRouter/useRoute),TypeScript 推断更强,导航守卫参数变了(to/from/next)。Vue 3 项目必须用 Vue Router 4。
Q 路由懒加载能提升多少性能?
A 首屏可减少 50-80% 加载时间(不加载未访问的页面)。大项目(10+ 页面)效果显著。
Q 路由守卫 meta 字段怎么用?
A 路由配置加 meta: { requiresAuth: true },守卫中用 to.meta.requiresAuth 访问。用于权限、标题、面包屑等。

📖 小节


📝 作业

  1. 基础题(难度⭐) 实现一个简单的多页 SPA:

    • 3 个页面:Home / About / Contact
    • 顶部导航 + 底部 router-view
    • vue-router 配置路由
  2. 进阶题(难度⭐⭐) 实现电商商品路由:

    • /products 列表页
    • /products/:id 详情页(动态参数)
    • /products/:id/edit 编辑页
    • 404 处理
  3. 挑战题(难度⭐⭐⭐) 实现完整的电商后台路由系统:

    1. 10+ 页面(home/products/orders/users/settings/login/404 等)
    2. 嵌套路由(/admin/dashboard, /admin/products 等)
    3. 路由懒加载(首屏快)
    4. 登录守卫(未登录跳 /login,登录后跳回)
    5. 5 种导航方式全覆盖
    6. 编程式导航(点击按钮跳转)
    7. TypeScript 强类型 routes 配置
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏