Appearance
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/#/path | http://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 router11. 实现原理深度解析
Hash 模式实现
初始化:
- 解析当前 URL 的 hash 值
- 匹配对应的路由
- 渲染组件
导航:
- 调用
router.push()或点击<router-link> - 修改 URL 的 hash 值
- 触发
hashchange事件 - 重新匹配路由并渲染组件
- 调用
监听:
- 监听
hashchange事件 - 当 hash 变化时,重新匹配路由
- 监听
History 模式实现
初始化:
- 解析当前 URL 的路径
- 匹配对应的路由
- 渲染组件
导航:
- 调用
router.push()或点击<router-link> - 使用
history.pushState()修改 URL - 匹配新的路由并渲染组件
- 调用
监听:
- 监听
popstate事件(浏览器前进/后退) - 当 URL 变化时,重新匹配路由
- 监听
路由匹配算法
- 路径解析:将 URL 路径解析为路径段数组
- 路由匹配:将路径段与路由配置进行匹配
- 参数提取:提取动态路径参数
- 组件渲染:渲染匹配到的组件
12. 最佳实践
- 使用路由懒加载:减小初始包大小,提高加载速度
- 合理使用导航守卫:实现权限控制、页面标题设置等
- 使用命名路由:提高代码可读性和维护性
- 使用路由元信息:存储路由相关的额外信息
- 配置滚动行为:提升用户体验
- 设置 404 路由:处理未匹配的路径
- 使用 History 模式:提供更友好的 URL
- 正确配置服务器:确保 History 模式正常工作
- 使用动态路由:处理参数化路径
- 优化路由性能:避免过度使用嵌套路由和复杂的路由配置
13. 总结
Vue Router 是 Vue.js 官方的路由管理器,提供了丰富的功能和灵活的配置选项。通过理解其核心 API 和实现原理,你可以构建出更加复杂和高效的单页应用。
Hash 模式和 History 模式各有优缺点,应根据具体项目需求选择合适的模式。在使用 History 模式时,需要注意服务器配置,确保所有请求都能正确指向应用的入口文件。
通过合理使用 Vue Router 的各种功能,如路由懒加载、导航守卫、路由元信息等,可以构建出更加健壮、性能更好的单页应用。