深入使用
导航守卫
在实际开发中,我们经常需要在路由跳转前或跳转后执行一些逻辑,比如权限验证。Vue Router 提供了丰富的导航守卫机制。
核心参数说明
to:即将进入的目标路由对象。from:当前导航正要离开的路由对象。next:控制路由跳转的函数。next():放行,继续执行路由。next(false):中断当前导航,如果浏览器 URL 已改变,则回退到from路由。next('/path'):跳转到指定路径。next(error):将控制权交给router.onError()。
组件内守卫 在组件内部定义的守卫,生命周期更贴近组件本身。
export default {
beforeRouteEnter(to, from, next) {
// 在路由进入前调用,此时组件实例尚未创建
next();
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但组件被复用时调用
next();
},
beforeRouteLeave(to, from, next) {
// 在路由离开前调用,常用于确认用户是否要离开页面
next();
}
};
路由独享守卫 直接在路由配置中定义,适用于特定路由的权限控制。
const routes = [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
if (isAdmin()) {
next();
} else {
next('/login');
}
}
}
];
全局前置守卫 最常用的一种,通常用于登录态校验。
router.beforeEach((to, from, next) => {
if (to.path === '/protected' && !isLoggedIn()) {
next('/login');
} else {
next();
}
});
全局后置钩子
通过 router.afterEach 设置,不会接收 next 函数,也不影响导航流程,适合做日志记录或统计。
router.afterEach((to, from) => {
console.log('路由已切换');
});
全局解析守卫
通过 router.beforeResolve 设置,在所有组件内守卫和异步路由组件被解析之后调用,优先级高于 beforeEach。
嵌套路由
构建层级关系的页面结构时,嵌套路由非常实用。它允许在一个组件内部再渲染另一个路由视图。
父组件模板
确保父组件中包含 <router-view> 来渲染子路由。
<template>
<div>
<h2>用户页面</h2>
<router-view></router-view>
</div>
</template>
定义嵌套路由
注意,以 / 开头的嵌套路径会被视为根路径,这允许利用组件嵌套而不必强制使用嵌套的 URL。
const routes = [
{
path: '/user',
component: User,
children: [
{ path: 'profile', component: UserProfile },
{ path: 'posts', component: UserPosts }
]
}
];
如需查看官方文档详情,可参考 Vue Router 嵌套路由指南。
重定向与别名
重定向
通过 routes 配置完成,主要用于将访问旧路径的用户引导至新路径。
- 相对重定向:根据目标路由动态计算路径。
- 命名重定向:直接跳转到具名路由。
const routes = [
// 将 /users/123/posts 重定向到 /users/123/profile
{
path: '/users/:id/posts',
redirect: to => to.path.replace(/posts$/, 'profile')
},
// 跳转到具名路由
{ path: '/home', redirect: { name: 'homepage' } }
];
别名 别名允许不同的 URL 映射到同一个组件,URL 不会改变,但匹配的是别名对应的路径。
const routes = [
{
path: '/',
component: Homepage,
alias: '/home'
}
];
若路由包含参数,需确保绝对别名中也包含这些参数,例如 alias: ['/:id']。
history 配置:指定历史模式
Vue Router 支持三种历史模式,选择取决于项目需求。
- Hash 模式 (
createWebHashHistory):默认模式。URL 带#,无需服务器配置,适合纯前端 SPA,但对 SEO 不友好。 - History 模式 (
createWebHistory):URL 简洁,无#,需要服务器配置(如 Nginx 回退到 index.html),适合对 SEO 有要求的项目。 - Memory 模式 (
createMemoryHistory):不与 URL 交互,无历史记录,适合 Node 环境或 SSR,需在app.use(router)后手动 push 初始导航。
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [...]
});
路由元信息(meta)
meta 字段可用于存储权限标识、页面标题等额外信息。
配置示例
const routes = [
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: { requiresAuth: true, title: '控制面板' }
}
];
在守卫中使用
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !isAuthenticated()) {
return { name: 'Login' };
}
});
在组件中获取
<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
console.log(route.meta.title); // "控制面板"
</script>
拓展
状态管理(Pinia / Vuex)
对于复杂或跨页面的数据传递,推荐使用状态管理库。
Pinia vs Vuex
- API 与易用性:Vuex 采用 Flux 风格,概念较多(State, Getter, Mutation, Action),学习曲线稍陡;Pinia 设计更简单,结合 Composition API,直观易上手。
- 响应式:Vuex 依赖 Vue 响应式系统且有特定约定;Pinia 完全基于 Vue 3 响应式系统,状态变化自动更新。
- DevTools 支持:两者均支持 Vue DevTools,Pinia 体验更友好。
- 生态:Vuex 资料丰富,存量项目多;Pinia 作为 Vue 3 官方推荐,生态正在快速扩展。
总结:大型应用且已有 Vuex 经验可继续使用;新项目或追求现代化开发建议尝试 Pinia。
Pinia 基本使用
安装插件以支持数据持久化:
npm install pinia pinia-plugin-persistedstate
创建 Store
// store.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
increment() {
this.count++;
},
async incrementAsync() {
await new Promise(resolve => setTimeout(resolve, 1000));
this.count++;
}
},
persist: {
enabled: true,
storage: window.localStorage
}
});
初始化 Pinia
// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import { createPersistedState } from 'pinia-plugin-persistedstate';
const app = createApp(App);
const pinia = createPinia();
pinia.use(createPersistedState());
app.use(pinia);
app.mount('#app');
Pinia 使用 Cookies 存储
虽然 Cookie 容量有限,但在某些场景下可用于简单的持久化。需注意手动处理读写逻辑。
export const useUserStore = defineStore('user', {
state: () => ({ name: null, age: null }),
actions: {
setName(name) {
this.name = name;
document.cookie = `name=${name}; path=/; max-age=3600`;
},
getName() {
const row = document.cookie.split('; ').find(r => r.startsWith('name='));
if (row) {
this.name = row.split('=')[1];
}
}
}
});


