API 参考
动态导入语法
javascript
import('path/to/component.vue')Webpack 魔法注释
| 注释 | 说明 |
|---|---|
/* webpackChunkName: "name" */ | 指定 chunk 名称 |
/* webpackPrefetch: true */ | 预加载 |
/* webpackPreload: true */ | 预加载(并行) |
/* webpackMode: "lazy" */ | 懒加载模式 |
滚动行为
基本用法
使用 scrollBehavior 配置路由切换时的滚动位置:
javascript
const router = new VueRouter({
routes: [...],
scrollBehavior(to, from, savedPosition) {
// 返回滚动位置
}
})参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
to | Route | 目标路由对象 |
from | Route | 来源路由对象 |
savedPosition | Object | null | 浏览器前进/后退时的保存位置 |
返回值类型
javascript
const router = new VueRouter({
routes: [...],
scrollBehavior(to, from, savedPosition) {
// 1. 返回 { x, y } - 滚动到指定位置
return { x: 0, y: 0 }
// 2. 返回 savedPosition - 恢复保存的位置
if (savedPosition) {
return savedPosition
}
// 3. 返回 { selector } - 滚动到锚点
if (to.hash) {
return { selector: to.hash }
}
// 4. 返回 false - 不滚动
return false
}
})常用滚动行为
滚动到顶部
javascript
scrollBehavior(to, from, savedPosition) {
return { x: 0, y: 0 }
}保持滚动位置
javascript
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}滚动到锚点
javascript
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash,
behavior: 'smooth' // 平滑滚动
}
}
}条件滚动
javascript
scrollBehavior(to, from, savedPosition) {
// 特定路由保持位置
if (to.meta.keepScroll) {
return savedPosition || false
}
// 默认滚动到顶部
return { x: 0, y: 0 }
}异步滚动
javascript
scrollBehavior(to, from, savedPosition) {
return new Promise(resolve => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 500)
})
}完整示例
javascript
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
component: Home,
meta: { title: '首页' }
},
{
path: '/article/:id',
component: Article,
meta: { title: '文章详情', keepScroll: true }
}
],
scrollBehavior(to, from, savedPosition) {
// 前进/后退时恢复位置
if (savedPosition) {
return savedPosition
}
// 滚动到锚点
if (to.hash) {
return {
selector: to.hash,
behavior: 'smooth',
offset: { x: 0, y: 60 } // 偏移量(固定头部高度)
}
}
// 特定路由保持位置
if (to.meta.keepScroll) {
return false
}
// 默认滚动到顶部
return { x: 0, y: 0, behavior: 'smooth' }
}
})路由元信息
定义元信息
在路由配置中通过 meta 属性定义:
javascript
const routes = [
{
path: '/home',
component: Home,
meta: {
title: '首页',
requiresAuth: false
}
},
{
path: '/admin',
component: Admin,
meta: {
title: '管理后台',
requiresAuth: true,
roles: ['admin', 'super_admin']
}
}
]访问元信息
javascript
// 在路由守卫中
router.beforeEach((to, from, next) => {
// 获取标题
document.title = to.meta.title || '默认标题'
// 检查是否需要认证
if (to.meta.requiresAuth) {
// ...
}
next()
})元信息继承
子路由会继承父路由的 meta:
javascript
const routes = [
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true },
children: [
{
path: 'user',
component: UserManage,
meta: { title: '用户管理' }
// 实际 meta: { requiresAuth: true, title: '用户管理' }
}
]
}
]路由记录遍历
使用 to.matched 遍历所有路由记录:
javascript
router.beforeEach((to, from, next) => {
// 检查任意层级是否需要认证
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated()) {
next('/login')
return
}
}
next()
})完整示例
javascript
const routes = [
{
path: '/',
component: Layout,
meta: { requiresAuth: true },
children: [
{
path: '',
name: 'Dashboard',
component: Dashboard,
meta: {
title: '仪表盘',
icon: 'dashboard',
breadcrumb: ['首页', '仪表盘']
}
},
{
path: 'user',
name: 'UserManage',
component: UserManage,
meta: {
title: '用户管理',
icon: 'user',
roles: ['admin'],
breadcrumb: ['首页', '用户管理']
}
},
{
path: 'user/:id',
name: 'UserDetail',
component: UserDetail,
meta: {
title: '用户详情',
hidden: true, // 不在菜单显示
activeMenu: '/user' // 高亮的菜单项
}
}
]
}
]
// 使用 meta 信息
router.beforeEach((to, from, next) => {
// 设置标题
const title = to.meta.title
document.title = title ? `${title} - 管理系统` : '管理系统'
// 权限检查
if (to.matched.some(record => record.meta.roles)) {
const roles = to.meta.roles
const userRole = store.state.user.role
if (!roles.includes(userRole)) {
next('/403')
return
}
}
// 认证检查
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isLoggedIn) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
return
}
}
next()
})过渡动效
基本用法
使用 <transition> 包裹 <router-view>:
Vue SFC
<template>
<div id="app">
<transition name="fade" mode="out-in">
<router-view />
</transition>
</div>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>过渡模式
| 模式 | 说明 |
|---|---|
out-in | 先离开再进入 |
in-out | 先进入再离开 |
| 无 | 同时进行 |
动态过渡
根据路由元信息设置不同过渡:
Vue SFC
<template>
<div id="app">
<transition :name="transitionName" mode="out-in">
<router-view :key="$route.fullPath" />
</transition>
</div>
</template>
<script>
export default {
data() {
return {
transitionName: 'fade'
}
},
watch: {
'$route'(to, from) {
// 根据路由层级决定过渡方向
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
// 或使用路由 meta 指定过渡
this.transitionName = to.meta.transition || 'fade'
}
}
}
</script>
<style>
/* 淡入淡出 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
/* 左右滑动 */
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: transform 0.3s ease;
position: absolute;
width: 100%;
}
.slide-left-enter-from {
transform: translateX(100%);
}
.slide-left-leave-to {
transform: translateX(-100%);
}
.slide-right-enter-from {
transform: translateX(-100%);
}
.slide-right-leave-to {
transform: translateX(100%);
}
</style>路由级过渡
在路由配置中指定过渡:
javascript
const routes = [
{
path: '/home',
component: Home,
meta: { transition: 'slide-up' }
},
{
path: '/about',
component: About,
meta: { transition: 'fade' }
}
]Vue SFC
<template>
<transition :name="$route.meta.transition || 'fade'" mode="out-in">
<router-view />
</transition>
</template>数据获取
导航后获取
进入路由后获取数据,显示加载状态:
Vue SFC
<template>
<div class="post">
<div v-if="loading" class="loading">加载中...</div>
<div v-else>
<h2>{{ post.title }}</h2>
<p>{{ post.content }}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
loading: false,
post: null
}
},
watch: {
'$route': 'fetchData'
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
this.loading = true
try {
const response = await fetch(`/api/post/${this.$route.params.id}`)
this.post = await response.json()
} catch (error) {
console.error('获取数据失败:', error)
} finally {
this.loading = false
}
}
}
}
</script>导航前获取
使用 beforeRouteEnter 在导航完成前获取数据:
Vue SFC
<script>
export default {
data() {
return {
post: null
}
},
beforeRouteEnter(to, from, next) {
// 此时组件实例未创建,无法访问 this
fetch(`/api/post/${to.params.id}`)
.then(response => response.json())
.then(post => {
// 通过回调访问组件实例
next(vm => {
vm.post = post
})
})
.catch(() => {
next('/404')
})
},
beforeRouteUpdate(to, from, next) {
// 路由参数变化时
this.post = null
fetch(`/api/post/${to.params.id}`)
.then(response => response.json())
.then(post => {
this.post = post
next()
})
.catch(() => {
next('/404')
})
}
}
</script>使用 Vuex
javascript
// store/modules/post.js
export default {
state: {
post: null,
loading: false
},
mutations: {
SET_POST(state, post) {
state.post = post
},
SET_LOADING(state, loading) {
state.loading = loading
}
},
actions: {
async fetchPost({ commit }, id) {
commit('SET_LOADING', true)
try {
const response = await fetch(`/api/post/${id}`)
const post = await response.json()
commit('SET_POST', post)
} finally {
commit('SET_LOADING', false)
}
}
}
}javascript
// 路由配置
const routes = [
{
path: '/post/:id',
component: Post,
beforeEnter: (to, from, next) => {
store.dispatch('post/fetchPost', to.params.id)
.then(() => next())
.catch(() => next('/404'))
}
}
]导航故障
导航失败类型
Vue Router 3.4+ 提供导航失败检测:
javascript
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
// NavigationFailureType 枚举
NavigationFailureType.aborted // 导航被守卫中止
NavigationFailureType.cancelled // 新导航开始了
NavigationFailureType.duplicated // 目标位置与当前位置相同检测导航失败
javascript
router.push('/admin').catch(failure => {
if (isNavigationFailure(failure)) {
console.log('导航失败类型:', failure.type)
console.log('目标路由:', failure.to)
console.log('来源路由:', failure.from)
}
})处理导航失败
javascript
// 全局处理
router.beforeEach((to, from, next) => {
// 某些条件下中止导航
if (shouldAbort) {
next(false) // 中止导航
return
}
next()
})
// 捕获导航失败
this.$router.push('/admin')
.catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.aborted)) {
console.log('导航被中止')
}
})重复导航处理
javascript
// Vue Router 3.1+ 会抛出 NavigationDuplicated 错误
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => {
if (err.name !== 'NavigationDuplicated') {
return Promise.reject(err)
}
})
}动态路由
添加路由
javascript
// 添加单个路由
router.addRoute({
path: '/new-route',
component: NewRoute
})
// 添加嵌套路由
router.addRoute('parentRoute', {
path: 'child',
component: ChildRoute
})删除路由
javascript
// 通过名称删除路由
router.removeRoute('routeName')查询路由
javascript
// 获取所有路由记录
router.getRoutes()
// 检查路由是否存在
router.hasRoute('routeName')动态路由示例
javascript
// 权限路由动态添加
export const asyncRoutes = [
{
path: '/admin',
name: 'Admin',
component: () => import('@/views/Admin.vue'),
meta: { roles: ['admin'] }
}
]
// 根据用户角色添加路由
export function addDynamicRoutes(roles) {
const routes = asyncRoutes.filter(route => {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
}
return true
})
routes.forEach(route => {
router.addRoute(route)
})
}路由 API 详解
router.resolve
解析路由位置,返回路由信息和 URL:
javascript
const resolved = router.resolve({
name: 'UserDetail',
params: { id: 123 },
query: { tab: 'profile' }
})
console.log(resolved.href) // '/user/123?tab=profile'
console.log(resolved.route) // 路由对象
console.log(resolved.location) // 位置对象router.currentRoute
获取当前路由对象:
javascript
// 在组件内
this.$route
// 在组件外
router.currentRouterouter.app
获取挂载的 Vue 根实例:
javascript
const app = router.app最佳实践
1. 合理组织路由文件
code
router/
├── index.js # 主路由文件
├── modules/ # 路由模块
│ ├── user.js
│ ├── product.js
│ └── admin.js
└── guards.js # 路由守卫2. 使用路由常量
javascript
// constants/routes.js
export const ROUTE_NAMES = {
HOME: 'Home',
USER_LIST: 'UserList',
USER_DETAIL: 'UserDetail'
}
export const ROUTE_PATHS = {
HOME: '/',
USER: '/user',
ADMIN: '/admin'
}3. 统一错误处理
javascript
// 路由错误处理
router.onError(error => {
console.error('路由错误:', error)
// 跳转到错误页面
router.push('/error')
})
// 处理懒加载失败
const lazyLoad = (view) => {
return () => import(`@/views/${view}.vue`).catch(() => {
router.push('/error')
})
}4. 路由过渡优化
Vue SFC
<template>
<!-- 使用 keep-alive 缓存 -->
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<keep-alive :include="cachedViews">
<component :is="Component" :key="$route.fullPath" />
</keep-alive>
</transition>
</router-view>
</template>常见问题
1. 如何在新窗口打开路由?
javascript
const route = router.resolve({ name: 'UserDetail', params: { id: 123 } })
window.open(route.href, '_blank')2. 如何获取当前路由的所有匹配记录?
javascript
// matched 包含所有嵌套的路由记录
const matched = this.$route.matched3. 如何判断路由是否激活?
javascript
// 使用 router-link 的 active-class
<router-link to="/user" active-class="active">用户</router-link>
// 或使用 $route 判断
computed: {
isActive() {
return this.$route.path === '/user'
}
}4. 如何监听路由参数变化?
javascript
watch: {
'$route.params.id': {
handler(newId, oldId) {
this.fetchData(newId)
},
immediate: true
}
}API 参考
Router 实例方法
| 方法 | 说明 |
|---|---|
push(location) | 导航到指定路由 |
replace(location) | 替换当前路由 |
go(n) | 前进或后退 |
back() | 后退 |
forward() | 前进 |
addRoute(route) | 添加路由 |
removeRoute(name) | 删除路由 |
getRoutes() | 获取所有路由 |
hasRoute(name) | 检查路由是否存在 |
resolve(location) | 解析路由位置 |
Route 对象属性
| 属性 | 说明 |
|---|---|
path | 当前路径 |
params | 路由参数 |
query | 查询参数 |
hash | URL hash |
fullPath | 完整路径 |
name | 路由名称 |
matched | 匹配的路由记录 |
meta | 路由元信息 |
redirectedFrom | 重定向来源 |