Vue Router是Vue.js的官方路由管理器,用于构建单页面应用(SPA)。它允许我们在不刷新页面的情况下切换视图。
# npm
npm install vue-router@4
# yarn
yarn add vue-router@4
# pnpm
pnpm add vue-router@4
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App)
.use(router)
.mount('#app')
<!-- App.vue -->
<template>
<div>
<nav>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
</nav>
<!-- 路由出口 -->
<router-view />
</div>
</template>
// 路由配置
const routes = [
{
path: '/user/:id',
name: 'User',
component: User
},
{
path: '/post/:id/:title',
component: Post
}
]
<!-- User.vue -->
<template>
<div>
<h2>用户ID: {{ $route.params.id }}</h2>
<h2>用户ID: {{ userId }}</h2>
</div>
</template>
<script setup>
import { useRoute } from 'vue-router'
import { computed } from 'vue'
const route = useRoute()
const userId = computed(() => route.params.id)
</script>
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
// 字符串路径
function goToAbout() {
router.push('/about')
}
// 对象
function goToUser() {
router.push({ path: '/user/123' })
}
// 命名路由
function goToUserNamed() {
router.push({ name: 'User', params: { id: 123 } })
}
// 带查询参数
function goToSearch() {
router.push({ path: '/search', query: { q: 'vue' } })
// 结果: /search?q=vue
}
// 替换当前历史记录
function replaceRoute() {
router.replace('/home')
}
// 前进后退
function goBack() {
router.go(-1) // 后退一步
}
function goForward() {
router.go(1) // 前进一步
}
</script>
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
path: '',
component: UserHome
},
{
path: 'profile',
component: UserProfile
},
{
path: 'posts',
component: UserPosts
}
]
}
]
<!-- User.vue -->
<template>
<div>
<h2>用户 {{ $route.params.id }}</h2>
<nav>
<router-link :to="`/user/${$route.params.id}`">首页</router-link>
<router-link :to="`/user/${$route.params.id}/profile`">资料</router-link>
<router-link :to="`/user/${$route.params.id}/posts`">文章</router-link>
</nav>
<router-view />
</div>
</template>
const routes = [
{
path: '/',
components: {
default: Home,
sidebar: Sidebar,
footer: Footer
}
}
]
<template>
<div>
<router-view />
<router-view name="sidebar" />
<router-view name="footer" />
</div>
</template>
const routes = [
{
path: '/',
component: () => import('../views/Home.vue')
},
{
path: '/about',
component: () => import('../views/About.vue')
},
{
path: '/user/:id',
component: () => import('../views/User.vue')
}
]
router.beforeEach((to, from, next) => {
console.log('导航到:', to.path)
console.log('来自:', from.path)
// 检查是否需要登录
if (to.meta.requiresAuth && !isLoggedIn()) {
next('/login')
} else {
next()
}
})
router.afterEach((to, from) => {
// 设置页面标题
document.title = to.meta.title || '默认标题'
})
const routes = [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
if (isAdmin()) {
next()
} else {
next('/login')
}
}
}
]
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
// 离开路由前
onBeforeRouteLeave((to, from) => {
const answer = window.confirm('确定要离开吗?未保存的更改将丢失。')
if (!answer) return false
})
// 路由更新时(同一组件,参数变化)
onBeforeRouteUpdate((to, from) => {
console.log('路由参数更新')
})
</script>
const routes = [
{
path: '/admin',
component: Admin,
meta: {
requiresAuth: true,
title: '管理后台',
roles: ['admin']
}
}
]
router.beforeEach((to, from, next) => {
// 访问元信息
if (to.meta.requiresAuth) {
// 检查认证
}
// 设置标题
document.title = to.meta.title || '默认标题'
next()
})
const routes = [
{
path: '/user/:id',
component: User,
props: true // 将params作为props传递
},
{
path: '/search',
component: Search,
props: route => ({ query: route.query.q }) // 函数模式
}
]
<!-- User.vue -->
<script setup>
defineProps(['id'])
</script>
<template>
<div>用户ID: {{ id }}</div>
</template>
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue'),
meta: { title: '首页' }
},
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue'),
meta: { title: '登录' }
},
{
path: '/dashboard',
component: () => import('../layouts/Dashboard.vue'),
meta: { requiresAuth: true },
children: [
{
path: '',
name: 'DashboardHome',
component: () => import('../views/dashboard/Home.vue'),
meta: { title: '控制台' }
},
{
path: 'profile',
name: 'Profile',
component: () => import('../views/dashboard/Profile.vue'),
meta: { title: '个人资料' }
},
{
path: 'settings',
name: 'Settings',
component: () => import('../views/dashboard/Settings.vue'),
meta: { title: '设置' }
}
]
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('../views/NotFound.vue'),
meta: { title: '404' }
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
const isLoggedIn = localStorage.getItem('token')
if (to.meta.requiresAuth && !isLoggedIn) {
next('/login')
} else {
next()
}
})
// 全局后置钩子
router.afterEach((to) => {
document.title = to.meta.title || 'Vue App'
})
export default router
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else if (to.hash) {
return { el: to.hash }
} else {
return { top: 0 }
}
}
})