文章目录
一、使用步骤
1、安装
vue2
bash
npm install vue-router@3
vue3
bash
npm install vue-router@4
2、创建路由实例
- **vue2:**使用
new VueRouter,模式设置mode: 'history' - **vue3:**使用
createRouter,模式设置history: createWebHistory()
vue2
js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from './components/Home.vue';
//import About from './components/About.vue';
Vue.use(VueRouter);
//路由懒加载
const routes = [
{ path: '/', component: Home },
{ path: '/about', component:resolve => require(['@/components/About.vue'],resolve) }
];
const router = new VueRouter({
mode: 'history',
routes // short for `routes: routes`
});
export default router;
vue3
"history" => createWebHistory()
"hash" => createWebHashHistory()
"abstract" => createMemoryHistory()
js
import { createRouter, createWebHistory } from 'vue-router';
import Home from './components/Home.vue';
import About from './components/About.vue';
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
];
const router = createRouter({
history: createWebHistory(),
routes, // 这里不需要再写 routes: routes 了,直接传入数组即可
});
export default router;
路由的核心原理
就是通过 Hash 模式 或 History 模式 来监听 URL 的变化,从而实现视图组件的动态切换。
- Hash 模式: 通过监听
window.onhashchange事件来感知 URL 中 # 后面内容的变化。 - History 模式: 基于
window.history.pushState和window.history.replaceStateAPI 来修改 URL,并通过popstate事件监听 URL 的变化。
3、在 Vue 应用中使用路由
Vue 2 和 Vue 3 (基本相同)。都需要创建一个路由器实例,并在根 Vue 实例中挂载它:
- vue2: 使用
new Vue创建应用实例,将router作为对象属性配置进去 - vue3: 使用
createApp创建应用实例,app.use(router)配置
vue2
js
import Vue from 'vue';
import App from './App.vue';
import router from './router'; // 引入路由配置
new Vue({
el: '#app',
router, // 使用路由配置
render: h => h(App)
});
vue3
js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 引入路由配置
const app = createApp(App); // 创建应用实例
app.use(router); // 使用路由配置
app.mount('#app'); // 挂载应用实例到 DOM 上
二、路由跳转
-
vue2: 只支持单一
router-view;使用this.$router实现路由跳转;this.$route获取路由参数信息 -
vue3: 支持作用域插槽
v-slot="{ Component };使用useRouter()实现路由跳转 ;useRoute()获取路由参数信息html//vue2 <router-view/> //vue3 <!-- 使用 v-slot 获取当前组件,便于添加过渡动画 --> <router-view v-slot="{ Component }"> <transition name="fade" mode="out-in"> <component :is="Component" /> </transition> </router-view>
vue2
在 Vue2 中,我们可以通过两种方式控制路由:声明式导航 和 编程式导航。
-
声明式导航 : 使用 组件生成可点击的链接,类似 HTML 的 标签,但不会引起页面刷新。
-
编程式导航: 则通过 this.$router 提供的方法(如 push, replace, go)实现逻辑跳转,适用于按钮事件或条件判断场景。
js1) this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面) 2) this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面) 3) this.$router.back(): 请求(返回)上一个记录路由 4) this.$router.go(-1): 请求(返回)上一个记录路由 5) this.$router.go(1): 请求下一个记录路由
同时,this.$route 对象提供了当前路由的详细信息(如路径、参数、查询等),可用于动态响应路由变化。
html
<template>
<div id="app">
<nav>
<!-- 声明式导航 -->
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<router-link :to="{ name: 'User', params: { id: 123 }}">User</router-link>
<!-- 活动路由样式 -->
<router-link to="/" exact-active-class="active">Home</router-link>
<!-- 动态传参 -->
<li v-for="m in messages" :key="m.id">
<router-link :to="`/home/message/detail/${m.id}`">{{m.title}}</router-link>
<button @click="pushShow(m.id)">push查看</button>
<button @click="replaceShow(m.id)">replace查看</button>
</li>
</nav>
<!-- 路由出口:匹配的组件将在此处渲染 -->
<router-view/>
</div>
</template>
<script>
export default {
methods: {
// 编程式导航
goToAbout() {
this.$router.push('/about')
},
goBack() {
this.$router.go(-1) // 后退一页
},
replaceRoute() {
this.$router.replace('/about') // 替换当前记录,无法后退
//替换路由参数,并刷新路由
window.location.reload()
},
pushShow (id) {
this.$router.push(`/home/message/detail/${id}`)
},
replaceShow(id) {
this.$router.replace(`/home/message/detail/${id}`)
}
},
// 访问路由信息
computed: {
currentRoute() {
return this.$route // 包含 path, params, query, meta 等
}
}
}
</script>
vue3
提供了 useRouter 和 useRoute 两个组合式函数,可在 setup() 中直接调用
useRouter:useRouter()得到router路由实例,包含路由跳转方法(push、go、replace)useRoute:useRoute()得到route路由对象,包含路由信息参数(path、params、hash、query)
使用步骤:
- 引入:
import { useRouter, useRoute } from 'vue-router' - 调用:
useRouter();useRoute()
html
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<!-- 使用 v-slot 获取当前组件,便于添加过渡动画 -->
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
<script>
import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
const goToAbout = () => { router.push('/about') }
const goBack = () => { router.go(-1) }
// 监听路由参数变化,实时获取新用户数据
watch(
() => route.params.id,
(newId) => {
fetchUserData(newId)
},
{ immediate: true }
)
return {
goToAbout,
goBack,
currentRoute: route
}
}
}
</script>
三、路由导航守卫
路由守卫是 Vue Router 的强大功能之一,允许你在导航过程中插入逻辑钩子,常用于权限校验、页面提示、数据预加载等场景。
- 全局守卫:作用于所有路由切换
beforeEach:路由前置时触发(用于登录验证)afterEach:不接收 next 函数,不能阻止导航,常用于访问分析、更改页面标题、声明页面等辅助功能beforeResolve:是获取数据或执行任何其他操作(进入所有页面后都执行的操作)的理想位置
- 路由独享守卫:仅针对特定路由 ,直接定义在某个路由配置上,适用于管理员页面等特殊权限控制
beforeEnter:路由进入前(用于权限控制)afterEnter:路由离开时(用于清除或存储一些信息)
- 组件独享守卫:写在组件内部 ,用于处理组件自身的生命周期与路由交互
beforeRouteEnter:不能访问 this,因为此时组件还没有被创建,通过传一个回调给 next 来访问组件实例next(vm=>{})beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用beforeRouteLeave:用来预防用户在还未保存修改前突然离开。该守卫可以通过返回 false 来取消导航
它们的执行顺序如下:
全局前置守卫 (beforeEach)
路由独享守卫 (beforeEnter)
组件内守卫 (beforeRouteEnter)
全局解析守卫 (beforeResolve)
全局后置守卫 (afterEach)
区别
- vue2: 路由导航守卫中必须调用 next() 才能继续导航 ,否则会被阻塞
- vue3: 不再强制要求调用 next() ,而是通过返回值来决定导航行为 ,可以直接返回一个路径字符串、布尔值或 false 来中断导航。并且提供两个组合式 API,
onBeforeRouteUpdate和onBeforeRouteLeave
vue2
全局守卫
js
//beforeEach:用于登录验证
router.beforeEach((to, from, next) => {
const isAuthenticated = checkAuth()
if (to.meta.requiresAuth && !isAuthenticated && to.name !== 'Login') {
next('/login') // 未登录则跳转至登录页
} else {
next() // 放行
}
})
//beforeResolve
router.beforeResolve((to, from, next) => {
next()
})
//afterEach
router.afterEach((to, from) => {
console.log(`页面跳转:${
from.path} → ${
to.path}`)
})
路由独享守卫:
将一个函数数组传递给 beforeEnter,用于不同的路由重用守卫
javascript
// 清除 query 参数
function removeQueryParams(to) {
if (Object.keys(to.query).length)
return { path: to.path, query: {}, hash: to.hash }
}
// 清除 hash 值
function removeHash(to) {
if (to.hash) return { path: to.path, query: to.query, hash: '' }
}
const routes = [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
if (!isAdmin()) {
next('/403')
} else {
next()
}
},
afterEnter:(){}
},
{
path: '/users/:id',
component: UserDetails,
beforeEnter: [removeQueryParams, removeHash]
}
]
组件独享守卫:
javascript
const User = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 此时尚未创建组件实例,不能访问 this
next(vm => {
// 通过 vm 访问组件实例,可用于初始化数据
})
},
beforeRouteUpdate(to, from, next) {
// 当前组件复用时触发(如 /user/1 → /user/2)
this.userData = null
next()
},
beforeRouteLeave(to, from, next) {
// 离开当前路由前询问用户
const answer = window.confirm('确定要离开吗?')
if (answer) {
next()
} else {
next(false) // 阻止导航
}
}
}
vue3
最大的变化是:不再强制要求调用 next() ,而是通过返回值来决定导航行为
可以直接返回一个路径字符串、布尔值或 false 来中断导航。
提供两个组合式 API :onBeforeRouteUpdate和onBeforeRouteLeave
全局守卫
javascript
router.beforeEach((to, from) => {
const isAuthenticated = checkAuth()
// 检查是否需要认证
if (to.meta.requiresAuth && !isAuthenticated) {
const token = localStorage.getItem('token')
if (!token) {
return '/login'
}
// 检查用户权限
const userRole = await getUserRole()
if (to.meta.roles && !to.meta.roles.includes(userRole)) {
return '/403'
}
}
// 不需要 return 或 next(),默认放行
})
组合式 API 中的导航守卫
javascript
<script setup lang="ts">
import { ref } from 'vue'
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
const userData = ref()
onBeforeRouteUpdate(async (to, from) => {
//仅当 id 更改时才获取用户信息
if (to.params.id !== from.params.id) {
userData.value = await fetchUser(to.params.id)
}
// 处理路由参数更新,例如刷新用户数据
await refreshUserData(to.params.id)
})
onBeforeRouteLeave((to, from) => {
const answer = window.confirm('确定要离开吗?')
// 取消导航并停留在当前页面
if (!answer) return false
})
</script>
四、路由传参
4 种传参方式:
- params 动态路由 :参数作为路径一部分 ,如 /user/123,SEO 友好,适合资源标识
- 必须使用
name跳转 - 刷新页面参数不会丢失(因为在 URL 中)
- 必须使用
- query 查询参数 :拼接在 URL 后面 ,如 ?id=123&name=tom,适合筛选、分页等可分享场景
- 可以用 path 或 name 跳转
- 刷新页面参数不会丢失
- 适合传递非敏感、可分享的数据
- props 解耦传参 :官方推荐,支持布尔、对象、函数三种模式 ,让组件与路由解耦,提高复用性
- 组件可独立测试,不依赖 $route
- 组件与路由解耦,提高复用性
- 官方推荐
- state 隐式传参 :Vue Router 4 新增,数据不显示在 URL ,适合临时敏感数据
- 参数不显示在 URL 中
- 刷新页面可能丢失(取决于实现方式)
- 适合传递临时、敏感数据
Vue2 和 Vue3 的主要区别:
- API 层面 :Vue3 使用
useRoute() 和 useRouter()组合式函数,替代 this.$route - 重大变更 :Vue Router 4 移除了隐式 params 传参 ,必须在路由中定义动态参数或改用 query/state
- 新增特性 :支持 history.state 传参,TypeScript 类型推导更完善
场景选择:
- 详情页用 params
- 筛选用 query
- 组件复用用 props
- 复杂数据用 Pinia 管理
params和query传参
javascript
// 路由配置
const routes = [
{ path: '/user/:id', name: 'User', component: User }
]
// 传参方式1:params,必须使用 name 跳转
this.$router.push({ name: 'User', params: { id: 123 } })
// 或
this.$router.push('/user/123')
// 接收参数
this.$route.params.id
//传参方式2:query,path或name跳转都行
// 传参方式
this.$router.push({ path: '/user', query: { id: 123, name: 'tom' } })
// 接收参数
this.$route.query.id
props 解耦传参
javascript
// 路由配置
const routes = [
// 布尔模式:params 自动映射为 props
{ path: '/user/:id', component: User, props: true },
// 对象模式:静态 props
{ path: '/user', component: User, props: { id: 123 } },
// 函数模式:灵活处理
{
path: '/user',
component: User,
props: route => ({ id: route.query.id, name: route.query.name })
}
]
// 组件中接收
export default {
props: ['id', 'name']
}
state 隐式传参
javascript
// Vue2 写法(不推荐,刷新丢失)
this.$router.push({ name: 'User', params: { secret: 'data' } })
// 注意:如果路由没有定义 :secret,刷新后会丢失
// Vue3 + HTML5 History state
this.$router.push({
path: '/user',
state: { secret: 'hidden data' } // Vue Router 4 新增
})
// 接收
history.state.secret
五、动态路由
区别
- vue2: 使用
router.addRoutes添加路由;通配符写法path: '*' - vue3: 使用
router.addRoute添加路由;router.removeRoute删除路由;router.hasRoute判断路由是否存在;通配符写法path: '/:pathMatch(.*)*'
vue2
1、定义路由配置
javascript
// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '../components/Home.vue';
Vue.use(Router);
const router = new Router({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '*', component: () => import('../components/NotFound.vue') } // 通配符路由
]
});
export default router;
2、动态添加路由,在main.js或相应的初始化文件中
javascript
router.addRoutes([
{ path: '/about', component: () => import('../components/About.vue') },
{ path: '/user/:id', component: () => import('../components/UserProfile.vue') }
]);
vue3
1、定义路由配置
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../components/Home.vue';
import NotFound from '../components/NotFound.vue';
const routes = [
{ path: '/', component: Home },
{ path: '/:pathMatch(.*)*', component: NotFound } // 通配符路由
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
2、动态添加路由,在main.js或相应的初始化文件中
javascript
// 在main.js或相应的初始化文件中
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 确保导入正确的router实例
import About from './components/About.vue'; // 示例组件导入,根据需要添加更多组件和路由定义。
const app = createApp(App);
app.use(router); // 使用router实例。注意:在Vue 3中,这是必须的。
app.mount('#app'); // 挂载应用。注意:在Vue 3中,这是必须的。
// 动态添加路由示例:在应用启动时或根据条件添加。例如,根据用户权限动态加载路由。
router.addRoute({ path: '/about/:id', component: () => import('@/components/About.vue') });
//删除路由
// 没有起名,name值为空时
const removeRoute = router.addRoute({ path: '/about/:id', component: () => import('@/components/About.vue') });
removeRoute()
//name命名时,命名为about
router.removeRoute('about')
//检查路由是否存在
router.hasRoute('about')
六、路由元信息meta
使用场景:
- 权限控制: 在路由守卫中检查用户权限,根据 meta 中定义的权限信息决定是否允许访问某个路由。
- 页面标题管理: 在应用中动态设置页面标题,可以将页面标题信息放在 meta 中,然后在全局前置守卫或组件中读取并设置。
- 动态添加 CSS 类或样式: 根据 meta 中定义的样式信息,动态地给页面添加特定的 CSS 类或样式。
- 跟踪分析: 在 meta 中添加用于分析的标签,比如来源页面、是否是营销活动页面等,用于跟踪用户行为。
区别
- vue2: 在路由表中配置meta信息,在全局守卫
router.beforeEach中判断及使用 - vue3: 两种使用方法
- 原生meta: 实现方式
- 使用provide和inject
- 使用Vue Router的beforeRouteEnter钩子或watch监听路由变化
vue-meta库: 引入并使用
- 原生meta: 实现方式
vue2
1、权限控制
javascript
const routes = [
{
path: '/user',
component: UserComponent,
meta: { requiresAuth: true, title: '用户信息' },
},
{
path: '/admin',
component: AdminComponent,
meta: { requiresAuth: true, title: '管理员面板', role: 'admin' },
},
{
path: '/public',
component: PublicComponent,
meta: { title: '公共页面' },
},
];
在全局守卫中判断
javascript
router.beforeEach((to, from, next) => {
// 假设从全局状态中获取用户角色
const userRole = store.getters.userRole;
// 检查路由是否需要认证
if (to.matched.some(record => record.meta.requiresAuth)) {
// 检查用户角色是否满足路由要求
if (to.meta.requiresAuth && userRole !== 'admin') {
// 如果用户角色不是管理员,则重定向到登录页面
next({ name: 'login' });
} else {
// 用户角色匹配,允许访问
next();
}
} else {
// 不需要认证的路由,直接允许访问
next();
}
});
2、页面标题管理
javascript
router.beforeEach((to, from, next) => {
// 设置页面标题
document.title = to.meta.title || '默认标题';
next();
});
3、动态添加 CSS 类
javascript
export default {
setup() {
const route = useRoute();
// 根据 meta 中的样式信息动态添加 CSS 类
const pageClass = computed(() => route.meta.pageClass || '');
return { pageClass };
},
};
模板中
html
<div :class="pageClass">我是加了 pageClass 样式的div哦</div>
vue3
1、vue3自带Meta方法
- 使用
provide和inject
html
//父组件
<template>
<div>
<h1>父组件</h1>
<ChildComponent />
</div>
</template>
<script>
import { provide, ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
setup() {
const metaInfo = ref({
title: '页面标题',
description: '页面描述'
});
provide('metaInfo', metaInfo);
return { metaInfo };
}
}
</script>
html
//子组件
<template>
<div>
<h2>子组件</h2>
</div>
</template>
<script>
import { inject, onMounted, onUnmounted } from 'vue';
export default {
setup() {
const metaInfo = inject('metaInfo');
const updateMeta = () => {
document.title = metaInfo.value.title;
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.content = metaInfo.value.description;
} else {
const meta = document.createElement('meta');
meta.name = "description";
meta.content = metaInfo.value.description;
document.getElementsByTagName('head')[0].appendChild(meta);
}
};
onMounted(updateMeta);
onUnmounted(() => {
// 清除或恢复原始meta标签,如果需要的话。例如:恢复默认的document.title。
});
}
}
</script>
- 使用Vue Router的
beforeRouteEnter钩子或watch监听路由变化
javascript
//路由表中配置
const routes = [
{
path: '/',
component: Home,
meta: { title: '首页', description: '这是首页的描述' }
},
// 其他路由...
];
javascript
//路由守卫中更新
import { useRouter } from 'vue-router';
import { watch, onBeforeMount, ref } from 'vue';
import { useTitle } from '@vueuse/core'; // 如果你使用vueuse包来管理标题的话。否则,你可以直接操作document.title。
export default {
setup() {
const router = useRouter();
const title = useTitle(); // 使用vueuse的useTitle来管理标题。你也可以直接操作document.title。
const description = ref(''); // 用于存储描述信息。如果不需要,可以忽略此行。
const head = ref(document.head); // 获取head元素以便操作meta标签。如果不需要,可以忽略此行。但通常你需要它来动态添加或更新meta标签。
const updateMeta = () => {
// 更新meta的方法。如果不需要,可以忽略此行。但通常你需要它来动态添加或更新meta标签。例如:添加或更新description。
}
2、使用vue-meta库
- 安装
bash
npm install vue-meta
yarn add vue-meta
- 在入口文件中配置
javascript
import { createApp } from 'vue'
import App from './App.vue'
// 引入配置
import { createMetaManager } from 'vue-meta'
const app = createApp(App)
//挂载配置
app.use(createMetaManager())
app.mount('#app')
- 组件中使用,
metaInfo选项来定义或修改页面的元数据;或者使用useMetaAPI
html
<template>
<div>Your Content Here</div>
</template>
<script>
export default {
metaInfo: {
// 用于SEO的标题
title: '我的页面标题',
// 用于SEO的描述
meta: [
{ vmid: 'description', name: 'description', content: '这是页面的描述' }
]
}
//或者用useMeta API
import { useMeta } from 'vue-meta'
useMeta({
title: '我的页面标题',
meta: [
{ vmid: 'description', name: 'description', content: '这是页面的描述' }
]
})
}
</script>
七、错误处理
javascript
// 全局路由错误监听
router.onError((error) => {
console.error('路由错误:', error)
})
// 导航失败处理
router.push('/some-path').catch(err => {
if (err.name !== 'NavigationDuplicated') {
console.error('导航失败:', err)
}
})
八、滚动行为
目标:路由切换时页面如何滚动。比如,当跳转到新路由时,页面滚动到某个位置;切换路由时页面回到之前的滚动位置
当创建路由实例时,我们只需要提供一个 scrollBehavior 方法:
javascript
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
使用场景
- 滚动到固定距离
- 滚动到元素位置
- 滚动到锚点位置
- 滚动到之前的位置
- 延迟滚动
javascript
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior (to, from, savedPosition) {
//滚动到固定距离
return { top: 0 }
//滚动到元素位置,如始终在元素 #main 上方滚动 10px
return {
// 也可以这么写
// el: document.getElementById('main'),
el: '#main',
top: -10,
}
//滚动到锚点位置
if (to.hash) {
return {
el: to.hash,
}
}
//滚动到之前的位置(返回 savedPosition,在按下浏览器 后退/前进 按钮,或者调用 router.go() 方法时,页面会回到之前的滚动位置)
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
//延迟滚动
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ left: 0, top: 0 })
}, 500)
})
}
})