各位观众老爷,晚上好!(或者早上好,中午好,取决于你啥时候看)。今天咱们聊聊Vue 3里面那个“带路党”—— Vue Router。这玩意儿,说白了,就是帮你管理页面跳转的,让你的SPA(Single Page Application,单页面应用)看起来像个多页面应用。
准备好了吗?发车啦!
第一站:路由的创建,从“无”到“有”
首先,得有路由实例,就像你得先有地图,才能知道怎么走。在Vue Router里面,这个“地图”就是createRouter
方法创建出来的。
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('./components/HelloWorld.vue') // 懒加载
},
{
path: '/about',
name: 'About',
component: () => import('./components/About.vue')
},
{
path: '/user/:id', // 动态路由
name: 'User',
component: () => import('./components/User.vue'),
props: true // 将路由参数作为props传递给组件
},
{
path: '/:pathMatch(.*)*', // 匹配所有未定义的路由
name: 'NotFound',
component: () => import('./components/NotFound.vue')
}
]
const router = createRouter({
history: createWebHistory(), // 路由模式,后面会讲
routes // 路由配置
})
export default router
这段代码做了啥?
- 引入必要的东西:
createRouter
和createWebHistory
是Vue Router提供的,一个用来创建路由实例,一个用来指定路由模式。 - 定义路由:
routes
是一个数组,每个元素都是一个路由对象。每个路由对象里面包含:path
: 路径,就是你在浏览器地址栏里看到的。name
: 路由的名字,方便你以后跳转的时候用。component
: 组件,就是你要显示的页面。这里用了懒加载,只有访问到这个路由的时候才会加载对应的组件,可以提高首屏加载速度。props
: 一个很有用的配置,可以让路由参数直接作为组件的props
传递。
- 创建路由实例:
createRouter
方法接收一个配置对象,里面包含history
和routes
。history
: 指定路由模式。常用的有createWebHistory
(HTML5 History API,地址栏看起来比较正常)和createWebHashHistory
(Hash模式,地址栏带#
)。routes
: 就是我们定义的路由配置。
- 导出路由实例: 方便你在其他地方使用。
路由模式大PK:History vs Hash
路由模式决定了你的URL长啥样。Vue Router提供了两种主要的模式:
模式 | URL 样式 | 优点 | 缺点 |
---|---|---|---|
createWebHistory |
/about ,/user/123 |
URL看起来更正常,符合传统习惯,利于SEO。 | 需要服务器端配置,否则刷新页面会404。 |
createWebHashHistory |
/#/about ,/#/user/123 |
兼容性好,不需要服务器端配置。 | URL带# ,看起来不太美观,不利于SEO。 |
选择哪种模式,取决于你的需求和服务器配置。一般来说,如果服务器端可以配置,推荐使用createWebHistory
。
第二站:路由匹配,找到你的“归宿”
当你访问一个URL的时候,Vue Router会根据你定义的路由配置,找到对应的组件。这个过程就是路由匹配。
- 静态路由匹配: 像
/
,/about
这样的路由,直接匹配路径。 - 动态路由匹配: 像
/user/:id
这样的路由,:
后面的id
是一个参数,可以匹配/user/123
,/user/456
等等。 - 通配符路由匹配: 像
/:pathMatch(.*)*
这样的路由,可以匹配所有未定义的路由。
动态路由参数的获取
动态路由的参数,可以通过$route.params
来获取。
<template>
<div>
<h1>User ID: {{ userId }}</h1>
</div>
</template>
<script>
import { useRoute } from 'vue-router';
import { defineComponent, computed } from 'vue';
export default defineComponent({
setup() {
const route = useRoute();
const userId = computed(() => route.params.id);
return {
userId,
};
},
});
</script>
或者,如果你在路由配置中设置了props: true
,可以直接作为组件的props
传递。
<template>
<div>
<h1>User ID: {{ id }}</h1>
</div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
props: {
id: {
type: String,
required: true
}
},
});
</script>
第三站:路由导航,想去哪儿就去哪儿
Vue Router提供了两种主要的导航方式:声明式导航和编程式导航。
- 声明式导航: 使用
<router-link>
组件,类似于<a>
标签。
<router-link to="/">Home</router-link>
<router-link :to="{ name: 'About' }">About</router-link>
<router-link :to="{ path: '/user/123' }">User 123</router-link>
<router-link :to="{ name: 'User', params: { id: 456 } }">User 456</router-link>
<router-link>
组件会自动阻止默认的链接跳转行为,并使用Vue Router的导航机制。
- 编程式导航: 使用
router.push
和router.replace
方法。
import { useRouter } from 'vue-router'
import { defineComponent } from 'vue';
export default defineComponent({
setup() {
const router = useRouter()
const goToHome = () => {
router.push('/')
}
const goToAbout = () => {
router.push({ name: 'About' })
}
const goToUser = (id) => {
router.push({ path: `/user/${id}` })
}
const replaceHome = () => {
router.replace('/') // 替换当前历史记录
}
return {
goToHome,
goToAbout,
goToUser,
replaceHome
}
}
})
router.push
会在历史记录中添加一条新的记录,而router.replace
会替换当前的记录。
第四站:导航守卫,站岗放哨的“保安”
导航守卫允许你在路由跳转前后做一些事情,比如权限验证,页面统计等等。Vue Router提供了三种类型的导航守卫:
- 全局守卫: 应用级别的守卫,在所有路由跳转前后都会执行。
beforeEach
: 在每次路由跳转前执行。afterEach
: 在每次路由跳转后执行。beforeResolve
: 在所有组件内守卫和异步路由组件被解析之后,导航被确认之前执行。
- 路由独享守卫: 只对单个路由生效的守卫。
beforeEnter
: 在进入该路由前执行。
- 组件内守卫: 在组件内部定义的守卫。
beforeRouteEnter
: 在进入该组件对应的路由前执行,不能访问组件实例this
。beforeRouteUpdate
: 在当前组件对应的路由改变时执行,比如动态路由参数改变。beforeRouteLeave
: 在离开该组件对应的路由前执行。
全局守卫的用法
router.beforeEach((to, from, next) => {
// to: 将要进入的路由对象
// from: 当前导航正要离开的路由对象
// next: 一个函数,调用它可以让导航进行下去
console.log('beforeEach: going to', to.name, 'from', from.name)
// 权限验证示例
const isAuthenticated = localStorage.getItem('token') // 假设token存在表示已登录
if (to.name !== 'Login' && !isAuthenticated) {
// 如果目标路由不是登录页面,并且用户未登录,则跳转到登录页面
next({ name: 'Login' })
} else {
next() // 允许导航
}
})
router.afterEach((to, from) => {
// to and from are both route objects.
console.log('afterEach: gone to', to.name, 'from', from.name)
// 页面统计示例
// trackPageView(to.path)
})
router.beforeResolve((to, from) => {
console.log('beforeResolve: going to', to.name, 'from', from.name)
return true; // 必须返回 true 或 调用 next()
});
路由独享守卫的用法
const routes = [
{
path: '/profile',
name: 'Profile',
component: () => import('./components/Profile.vue'),
beforeEnter: (to, from, next) => {
// 只有登录用户才能访问profile页面
const isAuthenticated = localStorage.getItem('token')
if (!isAuthenticated) {
next({ name: 'Login' })
} else {
next()
}
}
}
]
组件内守卫的用法
<template>
<div>
<h1>Profile</h1>
</div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this` !
// 因为当守卫执行前,组件实例还没被创建
console.log('beforeRouteEnter: going to profile from', from.name)
// 可以通过 next 回调访问组件实例
next(vm => {
// 通过 `vm` 访问组件实例
console.log('beforeRouteEnter: component instance', vm)
})
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数 /users/:id 的路由,当 /users/1 和 /users/2 之间跳转的时候,
// 由于会复用同样的 Profile 组件,因此组件的生命周期钩子不会被调用。
// 在这种情况下,可以使用 beforeRouteUpdate 钩子函数来监听路由的变化
console.log('beforeRouteUpdate: route changed from', from.name, 'to', to.name)
next()
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以用来阻止用户离开,比如在用户正在编辑表单的时候
const confirmLeave = window.confirm('Are you sure you want to leave?')
if (confirmLeave) {
next()
} else {
next(false) // 阻止导航
}
}
});
</script>
next
函数的重要性
在导航守卫中,next
函数非常重要。它决定了导航是否继续进行。
next()
: 允许导航继续进行。next(false)
: 阻止导航。next('/')
或next({ path: '/' })
: 跳转到指定的路由。next(error)
: 终止导航,并将错误传递给router.onError()
。
第五站:高级用法,锦上添花
- 路由元信息(Meta Fields): 可以在路由配置中添加
meta
字段,存储一些额外的信息,比如页面标题,权限等等。
const routes = [
{
path: '/admin',
name: 'Admin',
component: () => import('./components/Admin.vue'),
meta: {
requiresAuth: true, // 需要登录才能访问
title: 'Admin Panel'
}
}
]
然后在导航守卫中,可以访问to.meta
来获取这些信息。
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated) {
next({ name: 'Login' })
} else {
document.title = to.meta.title || 'My App' // 设置页面标题
next()
}
})
- 命名视图(Named Views): 可以在同一个页面中显示多个组件。
<template>
<div class="home">
<router-view name="default"></router-view>
<router-view name="sidebar"></router-view>
</div>
</template>
const routes = [
{
path: '/',
components: {
default: () => import('./components/Home.vue'),
sidebar: () => import('./components/Sidebar.vue')
}
}
]
- 路由别名(Alias): 可以给一个路由设置多个别名。
const routes = [
{
path: '/users',
component: () => import('./components/Users.vue'),
alias: ['/people', '/members']
}
]
总结
Vue Router是Vue 3中非常重要的一个组件,它可以帮助你轻松地管理页面跳转。通过学习路由的创建、匹配、导航和导航守卫,你可以构建出复杂的单页面应用。
希望今天的讲解对你有所帮助。记住,多写代码,才能真正掌握!下次再见!