Skip to content

Vue Router 详解

1. 什么是 Vue Router

Vue Router 是 Vue.js 官方的路由管理器。它与 Vue.js 核心深度集成,让构建单页应用变得简单。Vue Router 提供了以下功能:

  • 嵌套路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式
  • 可定制的滚动行为
  • 路由懒加载

2. 安装与基本使用

安装

bash
# 使用 npm
npm install vue-router@3

# 使用 yarn
yarn add vue-router@3

基本使用

javascript
// 1. 导入 Vue 和 Vue Router
import Vue from 'vue'
import VueRouter from 'vue-router'

// 2. 注册 Vue Router 插件
Vue.use(VueRouter)

// 3. 定义路由组件
const Home = {
  template: '<div>Home</div>'
}

const About = {
  template: '<div>About</div>'
}

// 4. 定义路由
const routes = [
  {
    path: '/',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]

// 5. 创建路由实例
const router = new VueRouter({
  routes
})

// 6. 创建 Vue 实例并挂载路由
new Vue({
  router
}).$mount('#app')

在模板中使用:

html
<template>
  <div>
    <h1>App</h1>
    <nav>
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </nav>
    <router-view></router-view>
  </div>
</template>

3. 核心 API

路由配置

基本路由配置

javascript
const routes = [
  {
    path: '/',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]

嵌套路由

javascript
const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      {
        // 当 /user/:id/profile 匹配成功
        path: 'profile',
        component: UserProfile
      },
      {
        // 当 /user/:id/posts 匹配成功
        path: 'posts',
        component: UserPosts
      }
    ]
  }
]

命名路由

javascript
const routes = [
  {
    path: '/user/:id',
    name: 'user',
    component: User
  }
]

使用命名路由:

html
<router-link :to="{ name: 'user', params: { id: 123 } }">User</router-link>

命名视图

javascript
const routes = [
  {
    path: '/',
    components: {
      default: Home,
      a: About,
      b: Contact
    }
  }
]

使用命名视图:

html
<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>

路由实例方法

router.push(location, onComplete?, onAbort?)

  • 作用:导航到新地址,向 history 栈添加一个新记录。
  • 使用场景:用于编程式导航。
javascript
// 字符串路径
router.push('/home')

// 对象
router.push({ path: '/home' })

// 命名路由
router.push({ name: 'user', params: { id: '123' } })

// 带查询参数
router.push({ path: '/register', query: { plan: 'private' } })

router.replace(location, onComplete?, onAbort?)

  • 作用:导航到新地址,替换当前 history 记录。
  • 使用场景:用于不需要用户通过浏览器后退按钮返回到前一个页面的情况。

router.go(n)

  • 作用:在 history 栈中向前或向后移动 n 步。
  • 使用场景:用于在历史记录中导航。
javascript
// 向前移动一步,相当于 history.forward()
router.go(1)

// 向后移动一步,相当于 history.back()
router.go(-1)

// 向前移动 3 步
router.go(3)

路由对象

路由对象包含以下属性:

  • $route.path:当前路由的路径,字符串,总是解析为绝对路径。
  • $route.params:一个对象,包含路由中的动态片段和全匹配片段。
  • $route.query:一个对象,包含 URL 查询参数。
  • $route.hash:当前路由的 hash 值(带 #)。
  • $route.fullPath:完成解析后的 URL,包含查询参数和 hash。
  • $route.matched:一个数组,包含当前路由匹配的所有路由记录。
  • $route.name:当前路由的名称(如果有)。

导航守卫

全局前置守卫

javascript
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // to: 即将要进入的目标路由对象
  // from: 当前导航正要离开的路由对象
  // next: 函数,必须调用,否则导航会被中断

  // 验证用户是否登录
  if (to.meta.requiresAuth && !isLoggedIn) {
    next('/login')
  } else {
    next()
  }
})

全局解析守卫

javascript
router.beforeResolve((to, from, next) => {
  // 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后调用
  next()
})

全局后置钩子

javascript
router.afterEach((to, from) => {
  // 导航完成后调用,没有 next 函数
  // 可以用于页面标题设置、分析等
  document.title = to.meta.title || 'Default Title'
})

路由独享守卫

javascript
const routes = [
  {
    path: '/user/:id',
    component: User,
    beforeEnter: (to, from, next) => {
      // 路由独享的守卫
      next()
    }
  }
]

组件内守卫

javascript
export default {
  beforeRouteEnter(to, from, next) {
    // 在组件被创建之前调用
    // 不能访问 this
    next(vm => {
      // 通过 vm 访问组件实例
    })
  },
  beforeRouteUpdate(to, from, next) {
    // 当前路由改变,但是组件被复用时调用
    // 可以访问 this
    next()
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问 this
    next()
  }
}

4. 路由模式

Hash 模式

特点

  • URL 中包含 # 符号,如 http://example.com/#/path
  • 不会向服务器发送请求,完全在客户端处理
  • 兼容性好,支持所有浏览器

实现原理

Hash 模式利用了浏览器的 hashchange 事件。当 URL 的 hash 部分(即 # 后面的部分)发生变化时,浏览器不会向服务器发送请求,而是触发 hashchange 事件。Vue Router 监听这个事件,根据新的 hash 值匹配对应的路由。

javascript
// 监听 hashchange 事件
window.addEventListener('hashchange', () => {
  const hash = window.location.hash.slice(1) // 去除 #
  // 根据 hash 匹配路由
  matchRoute(hash)
})

History 模式

特点

  • URL 中不包含 # 符号,如 http://example.com/path
  • 依赖 HTML5 History API
  • 需要服务器配置支持

实现原理

History 模式利用了 HTML5 History API 中的 pushState()replaceState() 方法。这些方法可以在不刷新页面的情况下修改浏览器的历史记录。Vue Router 监听浏览器的 popstate 事件(当用户点击浏览器的前进/后退按钮时触发),并根据当前 URL 匹配对应的路由。

javascript
// 使用 pushState 导航
function pushState(url) {
  window.history.pushState({}, '', url)
  // 根据新 URL 匹配路由
  matchRoute(url)
}

// 监听 popstate 事件
window.addEventListener('popstate', () => {
  const url = window.location.pathname
  // 根据 URL 匹配路由
  matchRoute(url)
})

两种模式的区别

特性Hash 模式History 模式
URL 格式http://example.com/#/pathhttp://example.com/path
浏览器兼容性所有浏览器支持 HTML5 History API 的浏览器
服务器配置不需要需要配置服务器,处理 404 情况
SEO 友好性较差较好
导航方式基于 hashchange 事件基于 pushState/replaceState 和 popstate 事件

服务器配置

使用 History 模式时,需要在服务器端配置,确保所有请求都指向同一个 HTML 文件。

Nginx 配置

nginx
location / {
  try_files $uri $uri/ /index.html;
}

Apache 配置

apache
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

Node.js (Express) 配置

javascript
const express = require('express')
const path = require('path')
const app = express()

app.use(express.static(path.join(__dirname, 'dist')))

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})

app.listen(3000)

5. 路由懒加载

路由懒加载可以减小初始包的大小,提高应用的加载速度。

基本用法

javascript
const routes = [
  {
    path: '/',
    component: () => import('./components/Home.vue')
  },
  {
    path: '/about',
    component: () => import('./components/About.vue')
  }
]

命名 chunk

javascript
const routes = [
  {
    path: '/',
    component: () =>
      import(/* webpackChunkName: "home" */ './components/Home.vue')
  },
  {
    path: '/about',
    component: () =>
      import(/* webpackChunkName: "about" */ './components/About.vue')
  }
]

6. 路由元信息

路由元信息可以用于存储路由相关的额外信息,如页面标题、是否需要认证等。

javascript
const routes = [
  {
    path: '/',
    component: Home,
    meta: {
      title: '首页',
      requiresAuth: false
    }
  },
  {
    path: '/dashboard',
    component: Dashboard,
    meta: {
      title: '仪表盘',
      requiresAuth: true
    }
  }
]

使用路由元信息:

javascript
router.beforeEach((to, from, next) => {
  // 设置页面标题
  document.title = to.meta.title || 'Default Title'

  // 检查是否需要认证
  if (to.meta.requiresAuth && !isLoggedIn) {
    next('/login')
  } else {
    next()
  }
})

7. 滚动行为

Vue Router 允许你自定义导航时的滚动行为。

javascript
const router = new VueRouter({
  routes,
  scrollBehavior(to, from, savedPosition) {
    // savedPosition 只有在 popstate 导航时才可用
    if (savedPosition) {
      return savedPosition
    } else {
      // 滚动到顶部
      return { x: 0, y: 0 }
    }
  }
})

8. 动态路由匹配

基本用法

javascript
const routes = [
  {
    path: '/user/:id',
    component: User
  }
]

在组件中获取参数:

javascript
export default {
  mounted() {
    console.log(this.$route.params.id)
  },
  watch: {
    '$route.params'(toParams, fromParams) {
      // 当路由参数变化时执行
      console.log('参数变化:', toParams, fromParams)
    }
  }
}

正则表达式

javascript
const routes = [
  // 匹配数字 ID
  {
    path: '/user/:id(\\d+)',
    component: User
  },
  // 匹配字母 ID
  {
    path: '/user/:id([a-zA-Z]+)',
    component: User
  }
]

可选参数

javascript
const routes = [
  // 可选参数
  {
    path: '/user/:id?',
    component: User
  }
]

重复参数

javascript
const routes = [
  // 重复参数
  {
    path: '/user/:id+',
    component: User
  }
]

9. 导航故障

当导航被阻止时,会触发导航故障。

javascript
router.push('/admin').catch(err => {
  if (err.name === 'NavigationDuplicated') {
    // 导航被阻止,因为已经在目标路由
  } else if (err.name === 'NavigationAborted') {
    // 导航被中止,因为前置守卫中调用了 next(false)
  }
})

10. 完整示例

基本配置

javascript
import Vue from 'vue'
import VueRouter from 'vue-router'

// 注册插件
Vue.use(VueRouter)

// 导入组件
const Home = () => import('./views/Home.vue')
const About = () => import('./views/About.vue')
const User = () => import('./views/User.vue')
const Dashboard = () => import('./views/Dashboard.vue')

// 定义路由
const routes = [
  {
    path: '/',
    name: 'home',
    component: Home,
    meta: {
      title: '首页'
    }
  },
  {
    path: '/about',
    name: 'about',
    component: About,
    meta: {
      title: '关于'
    }
  },
  {
    path: '/user/:id',
    name: 'user',
    component: User,
    meta: {
      title: '用户'
    }
  },
  {
    path: '/dashboard',
    name: 'dashboard',
    component: Dashboard,
    meta: {
      title: '仪表盘',
      requiresAuth: true
    }
  },
  // 404 路由
  {
    path: '*',
    redirect: '/'
  }
]

// 创建路由实例
const router = new VueRouter({
  mode: 'history', // 使用 history 模式
  base: process.env.BASE_URL,
  routes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { x: 0, y: 0 }
    }
  }
})

// 全局前置守卫
router.beforeEach((to, from, next) => {
  // 设置页面标题
  document.title = to.meta.title || 'Vue App'

  // 检查认证
  if (to.meta.requiresAuth && !localStorage.getItem('token')) {
    next('/')
  } else {
    next()
  }
})

// 全局后置钩子
router.afterEach((to, from) => {
  // 可以在这里添加页面访问统计等
  console.log(`导航到: ${to.path}`)
})

export default router

11. 实现原理深度解析

Hash 模式实现

  1. 初始化

    • 解析当前 URL 的 hash 值
    • 匹配对应的路由
    • 渲染组件
  2. 导航

    • 调用 router.push() 或点击 <router-link>
    • 修改 URL 的 hash 值
    • 触发 hashchange 事件
    • 重新匹配路由并渲染组件
  3. 监听

    • 监听 hashchange 事件
    • 当 hash 变化时,重新匹配路由

History 模式实现

  1. 初始化

    • 解析当前 URL 的路径
    • 匹配对应的路由
    • 渲染组件
  2. 导航

    • 调用 router.push() 或点击 <router-link>
    • 使用 history.pushState() 修改 URL
    • 匹配新的路由并渲染组件
  3. 监听

    • 监听 popstate 事件(浏览器前进/后退)
    • 当 URL 变化时,重新匹配路由

路由匹配算法

  1. 路径解析:将 URL 路径解析为路径段数组
  2. 路由匹配:将路径段与路由配置进行匹配
  3. 参数提取:提取动态路径参数
  4. 组件渲染:渲染匹配到的组件

12. 最佳实践

  1. 使用路由懒加载:减小初始包大小,提高加载速度
  2. 合理使用导航守卫:实现权限控制、页面标题设置等
  3. 使用命名路由:提高代码可读性和维护性
  4. 使用路由元信息:存储路由相关的额外信息
  5. 配置滚动行为:提升用户体验
  6. 设置 404 路由:处理未匹配的路径
  7. 使用 History 模式:提供更友好的 URL
  8. 正确配置服务器:确保 History 模式正常工作
  9. 使用动态路由:处理参数化路径
  10. 优化路由性能:避免过度使用嵌套路由和复杂的路由配置

13. 总结

Vue Router 是 Vue.js 官方的路由管理器,提供了丰富的功能和灵活的配置选项。通过理解其核心 API 和实现原理,你可以构建出更加复杂和高效的单页应用。

Hash 模式和 History 模式各有优缺点,应根据具体项目需求选择合适的模式。在使用 History 模式时,需要注意服务器配置,确保所有请求都能正确指向应用的入口文件。

通过合理使用 Vue Router 的各种功能,如路由懒加载、导航守卫、路由元信息等,可以构建出更加健壮、性能更好的单页应用。

基于 VitePress 的本地知识库