六:vue-router (重要)
(一). 对路由的理解
1.什么是路由
路由(Router) 是管理页面跳转和 URL 与视图映射关系的机制,核心作用是:根据不同的 URL 路径,展示对应的页面内容,实现单页应用(SPA)的页面切换。
一个路由就是一组对应关系(key—value)。
key为路径,value可能是function或component。
核心概念:
-
URL 与视图的映射
路由会将 URL 路径(如/home
、/about
)与对应的组件(页面)关联起来,当 URL 变化时,自动渲染匹配的组件,而无需刷新整个页面(这是 SPA 的核心特性)。 -
单页应用(SPA)的基础
传统多页应用通过服务器返回不同 HTML 页面实现跳转,而路由让 SPA 能在一个 HTML 页面内,通过切换组件模拟多页效果,提升用户体验(减少加载时间)。
以下是对图片内容的提取与整理,清晰区分后端路由和前端路由的核心差异:
2.路由分类
2.1. 后端路由
-
理解:
- 路由的
value
是函数,专门处理客户端(如浏览器)的请求。 - 作用:服务器接收到请求后,通过路径匹配对应的处理函数,返回数据或页面。
- 路由的
-
工作过程:
- 客户端(浏览器)发送请求(如
GET /api/users
)。 - 服务器根据请求路径,找到匹配的函数(如 Node.js 的
app.get('/api/users', (req, res) => { ... })
)。 - 函数处理请求(查数据库、拼接数据等),返回响应(如 JSON 数据、HTML 页面)。
- 客户端(浏览器)发送请求(如
2.2. 前端路由
-
理解:
- 路由的
value
是组件(component),用于动态展示页面内容(SPA 单页应用的核心)。 - 作用:在浏览器端实现页面切换,无需刷新整个页面。
- 路由的
-
工作过程:
- 浏览器的 URL 路径变化(如从
/home
到/about
)。 - 前端路由根据路径变化,匹配对应的组件(如 Vue 中
vue-router
的配置)。 - 匹配的组件渲染到页面,实现“局部更新”(无需服务器参与)。
- 浏览器的 URL 路径变化(如从
2.3核心差异对比
对比项 | 后端路由 | 前端路由 |
---|---|---|
处理位置 | 服务器端 | 浏览器端 |
value 类型 | 函数(处理请求、返回响应) | 组件(展示页面内容) |
页面刷新 | 每次请求可能刷新整个页面 | 仅局部更新,无整页刷新 |
典型技术 | Node.js(Express)、Java(SpringMVC) | Vue Router、React Router |
2.4总结
- 后端路由:负责“服务器如何响应请求”,用函数处理路径并返回数据/页面。
- 前端路由:负责“浏览器如何切换内容”,用组件匹配路径并局部更新页面。
前端路由是 SPA 应用的基础,让页面跳转像“切换组件”一样丝滑~
3.前端路由的实现原理
主要依赖两种浏览器特性:
hash
模式:利用 URL 中的#
后面的部分(如http://xxx.com/#/home
),hash
变化不会触发页面刷新,通过onhashchange
事件监听变化。history
模式:使用 HTML5 的history API
(如pushState
、replaceState
),可以修改 URL 且不发送请求,通过popstate
事件监听变化。
(二).路由高级
- 嵌套路由(
children
数组,配置子路由规则,对应组件内用<router-view>
显示 )- 路由守卫:全局守卫(
router.beforeEach
)、组件内守卫(beforeRouteEnter
等 )、路由独享守卫(beforeEnter
),用于权限校验、导航控制- 路由懒加载(
component: () => import('./MyRoute.vue')
):优化首屏加载速度
1.嵌套(多级)路由
1.嵌套路由,也叫子路由,是指在一个路由组件中,还可以再嵌套其他的路由组件。在 Vue Router 中,它能够帮助构建更加复杂且层次分明的单页应用结构,让页面的组织和导航更加清晰。
应用场景
当页面存在层级关系时,适合使用嵌套路由。比如电商网站中,商品详情页是一个大的页面,在商品详情页中,又可以有商品介绍、用户评价、相关推荐等子页面,这些子页面就可以通过嵌套路由来实现。
配置步骤(以 Vue Router 在 Vue 项目中的使用为例)
1. 定义路由配置
在 router/index.js
文件中(假设这是路由配置文件),进行如下配置:
import Vue from 'vue'
import VueRouter from 'vue-router'
import ParentComponent from '../components/ParentComponent.vue'
import ChildComponent1 from '../components/ChildComponent1.vue'
import ChildComponent2 from '../components/ChildComponent2.vue'Vue.use(VueRouter)const routes = [{path: '/parent',component: ParentComponent,children: [{path: 'child1',component: ChildComponent1},{path: 'child2',component: ChildComponent2}]}
]const router = new VueRouter({routes
})export default router
在上述代码中,定义了一个父路由 '/parent'
,其对应的组件是 ParentComponent
。在 children
数组中,又定义了两个子路由 'child1'
和 'child2'
,分别对应 ChildComponent1
和 ChildComponent2
。
2. 父组件中设置路由出口
在 ParentComponent.vue
中,需要设置 <router-view>
来显示子路由对应的组件:
<template><div><h1>这是父组件</h1><router-link to="/parent/child1">子组件1</router-link><router-link to="/parent/child2">子组件2</router-link><router-view></router-view></div>
</template>
这里通过 <router-link>
创建了导航链接,点击链接时,会在 <router-view>
中显示对应的子路由组件。
访问嵌套路由
当访问 http://localhost:8080/parent/child1
时,会显示 ParentComponent
的内容,同时在 <router-view>
中渲染 ChildComponent1
的内容;访问 http://localhost:8080/parent/child2
时,则渲染 ChildComponent2
的内容。
嵌套路由的注意事项
- 子路由路径:子路由的路径不要加
/
开头,否则会被当作根路径。 - 嵌套层级:可以根据实际需求进行多层嵌套,但层级过深可能会使代码复杂度增加,维护起来更困难。
- 路由匹配规则:当访问父路由时,如果没有匹配到具体的子路由,
<router-view>
中不会渲染任何内容,有时需要设置一个默认子路由来避免这种情况,比如:
{path: '/parent',component: ParentComponent,children: [{path: '', // 空字符串表示默认子路由component: ChildComponent1},{path: 'child1',component: ChildComponent1},{path: 'child2',component: ChildComponent2}]
}
这样,当访问 /parent
时,会默认显示 ChildComponent1
的内容。
通过嵌套路由,可以让单页应用的页面结构更加清晰合理,提升用户体验和代码的可维护性。
2.路由守卫
在 Vue Router 中,路由守卫是一种非常强大的功能,用于在路由跳转的不同阶段对导航进行拦截和控制,例如进行权限验证、记录日志、动态修改路由等。路由守卫主要分为全局守卫、路由独享守卫和组件内守卫三类。以下是详细介绍:
2.1全局守卫
全局守卫会对应用中所有的路由跳转起作用,主要包括以下三种:
router.beforeEach
- 触发时机:在路由跳转之前,每次路由切换都会触发,并且是全局前置守卫,可以对即将发生的导航进行拦截。
- 参数:接收三个参数,分别是
to
(即将要进入的目标路由对象)、from
(当前导航正要离开的路由对象)、next
(一个函数,必须调用它来resolve这个钩子。执行顺序决定了路由是否继续跳转 )。 - 示例:
const router = new VueRouter({...})router.beforeEach((to, from, next) => {// 模拟权限验证,假设用户登录后才有权限访问除登录页外的其他页面const isLoggedIn = localStorage.getItem('token'); if (to.path!== '/login' &&!isLoggedIn) {next('/login'); // 未登录则重定向到登录页} else {next(); // 允许访问,继续跳转}
})
router.beforeResolve
- 触发时机:在导航被确认之前,在所有组件内守卫和异步路由组件被解析之后,
beforeEach
之后触发。主要用于确保在导航确认之前,所有异步操作都已经完成。 - 参数:和
beforeEach
一样,包含to
、from
、next
。 - 示例:
- 触发时机:在导航被确认之前,在所有组件内守卫和异步路由组件被解析之后,
router.beforeResolve((to, from, next) => {if (to.meta.requiresAsyncData) {// 假设这里有一个异步获取数据的函数fetchAsyncData(to.params.id).then(() => {next();}).catch(() => {next(false);});} else {next();}
});
router.afterEach
- 触发时机:在路由跳转完成之后触发,没有
next
函数,不能对导航进行拦截,主要用于进行一些不需要对导航进行干预的操作,比如记录页面访问日志。 - 参数:接收两个参数,分别是
to
(成功跳转的目标路由对象)、from
(离开的路由对象)。 - 示例:
- 触发时机:在路由跳转完成之后触发,没有
router.afterEach((to, from) => {console.log(`从 ${from.path} 跳转到了 ${to.path}`);// 可以在这里调用接口记录页面访问日志等操作
})
在 Vue Router 中,全局守卫是作用于整个应用所有路由的导航钩子,用于统一处理路由跳转的权限验证、日志记录、数据预处理等逻辑。全局守卫分为三类,分别在路由跳转的不同阶段触发,以下是详细解析:
一、全局前置守卫(beforeEach
)
作用
在路由跳转前触发,是全局守卫中最常用的一种,主要用于拦截导航、验证权限、重定向等操作。
触发时机
- 每次路由切换(包括初始化时的首次加载)都会触发。
- 执行顺序:在所有组件内守卫和路由独享守卫之前。
参数
to
:即将进入的目标路由对象(包含路径、参数、元信息等)。from
:当前正要离开的路由对象。next
:函数,必须调用以决定导航行为:next()
:允许导航继续。next(false)
:取消导航,停留在当前页面。next('/path')
或next({ name: 'routeName' })
:重定向到指定路由。next(error)
:传递错误对象,会被全局错误处理器捕获。
示例(权限验证)
const router = new VueRouter({... })// 全局前置守卫:验证用户是否登录
router.beforeEach((to, from, next) => {// 1. 登录页无需验证,直接放行if (to.path === '/login') {next();return;}// 2. 非登录页:检查是否有登录凭证(如 token)const token = localStorage.getItem('token');if (token) {// 已登录:允许进入目标路由next();} else {// 未登录:强制重定向到登录页next('/login');}
});
二、全局解析守卫(beforeResolve
)
作用
在导航被确认前触发,用于确保所有异步操作(如组件加载、数据请求)完成后再进入目标路由。
触发时机
- 在
beforeEach
之后,afterEach
之前。 - 会等待所有组件内守卫和异步路由组件解析完成后才执行。
参数
与 beforeEach
相同(to
、from
、next
)。
示例(预处理数据)
// 全局解析守卫:确保数据加载完成
router.beforeResolve(async (to, from, next) => {// 如果路由需要预加载数据(通过 meta 标记)if (to.meta.needPreload) {try {// 异步加载数据await fetchData(to.params.id);next(); // 数据加载完成,允许导航} catch (error) {next('/error'); // 加载失败,重定向到错误页}} else {next(); // 无需预加载,直接放行}
});
三、全局后置钩子(afterEach
)
作用
在路由跳转完成后触发,主要用于执行不需要干预导航的操作(如日志记录、页面滚动)。
触发时机
- 导航已完成,目标组件已渲染。
- 无
next
函数,无法拦截或修改导航。
参数
to
:成功进入的目标路由对象。from
:离开的路由对象。
示例(记录访问日志)
// 全局后置钩子:记录页面访问日志
router.afterEach((to, from) => {// 1. 打印跳转信息console.log(`从 ${from.path} 跳转到 ${to.path}`);// 2. 调用接口记录日志(实际项目中使用 axios 等工具)fetch('/api/log', {method: 'POST',body: JSON.stringify({fromPath: from.path,toPath: to.path,time: new Date().toISOString()})});// 3. 页面滚动到顶部window.scrollTo(0, 0);
});
四、全局守卫的执行顺序
- 触发路由跳转 →
beforeEach
(全局前置) - 加载异步路由组件(若有)
- 执行目标路由的路由独享守卫(
beforeEnter
) - 执行目标组件内的
beforeRouteEnter
守卫 - 所有异步操作完成 →
beforeResolve
(全局解析) - 导航确认,组件渲染 →
afterEach
(全局后置)
五、核心使用场景
beforeEach
:权限验证、登录拦截、动态路由生成。beforeResolve
:路由跳转前的异步数据预处理。afterEach
:页面访问日志、滚动位置重置、埋点统计。
六、注意事项
- 避免无限循环:在
beforeEach
中重定向时,需确保条件判断正确(如登录页不重定向自身)。 - 异步操作处理:若有异步逻辑(如接口请求),需用
async/await
包裹,并在完成后调用next()
。 - 性能影响:全局守卫会拦截所有路由跳转,复杂逻辑可能影响性能,建议保持简洁。
全局守卫是 Vue Router 中实现统一导航控制的核心工具,通过合理组合三类守卫,可实现从权限验证到数据处理的全流程管控。
2.2路由独享守卫
可以为某个特定的路由配置守卫,只对该路由生效。
beforeEnter
- 触发时机:在进入对应的路由之前触发。
- 参数:接收三个参数,分别是
to
(即将要进入的目标路由对象)、from
(当前导航正要离开的路由对象)、next
(和全局守卫中的next
函数作用相同)。 - 示例:
const router = new VueRouter({routes: [{path: '/admin',component: Admin,beforeEnter: (to, from, next) => {// 只有管理员角色才能访问const userRole = localStorage.getItem('role'); if (userRole === 'admin') {next();} else {next('/forbidden'); // 无权限则重定向到禁止访问页面}}}]
})
在 Vue Router 中,路由独享守卫是一种仅作用于特定路由配置的导航钩子,允许你在某个路由跳转前进行定制化拦截和处理,而不必影响其他路由。以下是其核心用法与场景:
一、核心语法(beforeEnter
)
const router = new VueRouter({routes: [{path: '/admin',component: AdminPanel,beforeEnter: (to, from, next) => {// 路由独享守卫逻辑if (!isAdmin) {next('/login'); // 无权限则重定向} else {next(); // 允许访问}}}]
});
二、参数与执行流程
1. 参数
to
:目标路由对象(包含路径、参数、元信息等)。from
:当前路由对象。next
:控制导航行为的函数(同全局守卫的next
)。
2. 执行时机
- 在全局前置守卫(
beforeEach
)之后,组件内守卫(beforeRouteEnter
)之前执行。 - 仅在进入该路由时触发,离开时不触发。
三、典型应用场景
1. 权限控制(如管理员页面)
{path: '/admin',component: AdminPanel,beforeEnter: (to, from, next) => {const userRole = localStorage.getItem('role');if (userRole === 'admin') {next(); // 管理员允许访问} else {next('/forbidden'); // 非管理员跳转至禁止页}}
}
2. 数据预处理
{path: '/profile/:id',component: UserProfile,beforeEnter: async (to, from, next) => {try {// 预加载用户数据const user = await fetchUser(to.params.id);// 将数据存入路由元信息,供组件使用to.meta.user = user;next();} catch (error) {next('/404'); // 加载失败跳转 404}}
}
3. 导航条件判断
{path: '/payment',component: PaymentPage,beforeEnter: (to, from, next) => {// 检查购物车是否有商品const hasItems = localStorage.getItem('cartItems');if (hasItems) {next();} else {next('/cart'); // 无商品则跳转购物车}}
}
四、与其他守卫的对比
守卫类型 | 作用范围 | 触发时机 | 适用场景 |
---|---|---|---|
全局前置守卫 | 所有路由 | 路由跳转前 | 统一权限验证、登录拦截 |
路由独享守卫 | 单个路由配置 | 进入特定路由前 | 特定页面的权限控制、数据预加载 |
组件内守卫 | 组件内部 | 组件实例相关的导航阶段 | 组件级别的导航逻辑 |
五、注意事项
-
只作用于直接匹配的路由
如果路由配置了子路由,beforeEnter
仅在进入该路由时触发,不会作用于子路由。 -
避免重复逻辑
若多个路由需要相同的守卫逻辑,建议使用全局守卫或高阶函数封装,减少代码冗余。 -
异步操作处理
若守卫中包含异步请求(如验证 Token),需使用async/await
确保数据加载完成后再调用next()
。
六、总结
路由独享守卫是 Vue Router 中实现细粒度导航控制的灵活工具,适合在不影响全局导航的前提下,对特定路由添加定制化的拦截逻辑。合理使用它可提升代码的可维护性,避免全局守卫过于臃肿。
2.3组件内守卫
在路由组件内部定义的守卫,只在当前组件对应的路由跳转时生效。
beforeRouteEnter
- 触发时机:在路由进入该组件之前触发,此时组件实例还未被创建,所以不能访问
this
。 - 参数:接收三个参数,分别是
to
(即将要进入的目标路由对象)、from
(当前导航正要离开的路由对象)、next
(next
函数可以接收一个回调函数,在组件实例创建好之后执行 )。 - 示例:
- 触发时机:在路由进入该组件之前触发,此时组件实例还未被创建,所以不能访问
export default {beforeRouteEnter(to, from, next) {// 模拟获取数据后再进入组件getData().then(data => {next(vm => {vm.$data.data = data; // vm 是组件实例});}).catch(() => {next(false);});}
}
beforeRouteUpdate
- 触发时机:在当前路由改变,但是该组件被复用时调用,比如在带有动态参数的路由中,当参数变化时,组件会被复用,此时会触发该守卫。
- 参数:接收三个参数,分别是
to
(即将要进入的目标路由对象)、from
(当前导航正要离开的路由对象)、next
(和其他守卫中的next
函数作用相同)。 - 示例:
export default {beforeRouteUpdate(to, from, next) {// 当路由参数变化时,重新获取数据if (to.params.id!== from.params.id) {this.fetchNewData(to.params.id);}next();}
}
beforeRouteLeave
- 触发时机:在导航离开该组件时触发,可用于询问用户是否确认离开,比如用户在表单中输入了内容还未保存时。
- 参数:接收三个参数,分别是
to
(即将要进入的目标路由对象)、from
(当前导航正要离开的路由对象)、next
(和其他守卫中的next
函数作用相同)。 - 示例:
export default {beforeRouteLeave(to, from, next) {if (this.hasUnsavedChanges) {const confirmLeave = window.confirm('你有未保存的更改,确定要离开吗?');if (confirmLeave) {next();} else {next(false);}} else {next();}}
}
通过合理运用这些路由守卫,可以实现丰富且强大的导航控制逻辑,提升应用的安全性和用户体验。
在 Vue Router 中,组件内守卫是直接定义在路由组件内部的导航钩子,用于控制该组件实例相关的路由导航行为。与全局守卫和路由独享守卫不同,组件内守卫仅作用于当前组件,提供更精细的导航控制。以下是其核心用法与场景:
一、三种组件内守卫
1. beforeRouteEnter
- 触发时机:在路由进入组件前触发,此时组件实例尚未创建,因此无法访问
this
。 - 用途:可通过
next
回调访问组件实例,常用于在路由进入前预加载数据。 - 示例:
export default {beforeRouteEnter(to, from, next) {// 异步获取数据(如用户信息)fetchUser(to.params.id).then(user => {// 通过 next 回调访问组件实例next(vm => {vm.user = user; // 将数据传递给组件实例});});} }
2. beforeRouteUpdate
- 触发时机:在当前路由改变(如参数变化)但组件被复用时触发,此时组件实例已存在,可访问
this
。 - 用途:常用于动态参数路由(如
/user/:id
)的更新逻辑,避免组件重复创建。 - 示例:
export default {watch: {// 传统方式:监听 $route 变化$route(to) {this.fetchData(to.params.id);}},// 更优解:使用组件内守卫beforeRouteUpdate(to, from, next) {// 参数变化时重新加载数据if (to.params.id !== from.params.id) {this.fetchData(to.params.id);}next();} }
3. beforeRouteLeave
- 触发时机:在导航离开当前组件时触发,可访问
this
。 - 用途:常用于阻止用户意外离开(如未保存表单),或清理资源。
- 示例:
export default {data() {return {formDirty: false // 表单是否已修改};},beforeRouteLeave(to, from, next) {if (this.formDirty) {const confirmLeave = window.confirm('表单尚未保存,确定离开?');confirmLeave ? next() : next(false); // 确认后允许离开} else {next(); // 未修改直接放行}} }
二、组件内守卫的执行顺序
当路由发生变化时,守卫的触发顺序为:
- 全局:
beforeEach
- 路由独享:
beforeEnter
- 组件内:
- 当前组件的
beforeRouteLeave
(若离开当前组件) - 目标组件的
beforeRouteEnter
(若进入新组件)
- 当前组件的
- 全局:
beforeResolve
- 全局:
afterEach
三、典型应用场景
1. 数据预加载
export default {beforeRouteEnter(to, from, next) {// 进入组件前预加载文章数据fetchArticle(to.params.id).then(article => {next(vm => {vm.article = article;});});}
}
2. 动态路由参数更新
export default {beforeRouteUpdate(to, from, next) {// 当路由参数变化时(如从 /user/1 到 /user/2)this.fetchUser(to.params.id);next();}
}
3. 防止数据丢失
export default {beforeRouteLeave(to, from, next) {// 检查是否有未保存的草稿if (this.hasDraft) {saveDraft().then(() => next());} else {next();}}
}
四、与其他守卫的对比
守卫类型 | 定义位置 | 能否访问 this | 适用场景 |
---|---|---|---|
全局守卫 | 路由配置文件 | 否 | 全局权限验证、日志记录 |
路由独享守卫 | 路由配置对象 | 否 | 特定路由的权限控制 |
组件内守卫 | 组件内部 | beforeRouteEnter 不可,其他可 | 组件级的导航逻辑、数据预加载 |
五、注意事项
-
beforeRouteEnter
中访问组件实例
由于此时组件尚未创建,需通过next(vm => { ... })
回调访问实例。 -
避免重复逻辑
若多个组件需要相同的守卫逻辑,可使用 mixin 或全局守卫减少代码冗余。 -
异步操作处理
若守卫中包含异步请求(如验证 Token),需确保数据加载完成后再调用next()
。
六、总结
组件内守卫是 Vue Router 中实现组件级导航控制的强大工具,通过 beforeRouteEnter
、beforeRouteUpdate
和 beforeRouteLeave
三个钩子,可精确控制组件实例的导航生命周期。合理使用组件内守卫能提升用户体验(如防止数据丢失),并优化组件性能(如复用组件时的数据更新)。
3.history模式与hash模式
在 Vue Router 中,history
模式和**hash
模式**是两种不同的路由实现方式,主要区别在于 URL 的表现形式和浏览器历史记录的处理方式。以下是详细对比与选择建议:
一、核心区别
特性 | hash 模式(默认) | history 模式 |
---|---|---|
URL 格式 | http://example.com/#/home | http://example.com/home |
路径标识符 | 使用 # 符号分隔路径(如 #/user/1 ) | 直接使用真实路径(如 /user/1 ) |
历史记录 | hash 变化会触发浏览器历史记录,但不会向服务器发送请求 | 依赖 HTML5 History API(pushState /replaceState ),路径变化会被记录到历史中 |
服务器配置 | 无需特殊配置,所有请求都指向根页面 | 需要服务器配置,确保所有路由请求都返回同一个 HTML 文件 |
兼容性 | 支持所有浏览器(包括 IE9+) | 依赖 HTML5 History API,IE9 及以下不支持 |
二、hash 模式详解
- 工作原理
- URL 中的
#
及其后的内容被视为浏览器的hash
值,不会被发送到服务器。 - 当
hash
变化时(如#/home
→#/about
),Vue Router 会拦截并根据hash
值渲染对应组件。
- 优点
- 无需服务器配置:所有请求都指向根页面(如
index.html
),适合快速开发和静态网站。 - 兼容性强:支持所有现代浏览器,甚至包括 IE9。
- 缺点
- URL 不美观:
#
符号在 URL 中较为突兀,不符合传统 URL 格式。 - SEO 不友好:搜索引擎爬虫通常会忽略
hash
值,不利于页面收录。
三、history 模式详解
- 工作原理
- 使用 HTML5 的
pushState
和replaceState
API 操作浏览器历史记录,URL 看起来像传统的静态页面路径(如/home
)。 - 当用户访问
/user/1
时,浏览器会向服务器发送请求,但实际上所有路径都应返回同一个 HTML 文件(如index.html
),由 Vue Router 在客户端处理路由逻辑。
- 优点
- URL 更优雅:没有
#
符号,符合传统 URL 格式,提升用户体验。 - SEO 友好:搜索引擎可以直接抓取真实路径,有利于页面收录。
- 缺点
- 服务器配置复杂:需要服务器配置所有路由请求都返回
index.html
,否则会出现 404 错误(见下方服务器配置示例)。 - 兼容性较差:不支持 IE9 及以下浏览器。
四、服务器配置示例(history 模式)
- Node.js (Express)
const express = require('express');
const app = express();// 静态文件处理
app.use(express.static(__dirname + '/dist'));// 所有路由请求都返回 index.html
app.get('*', function(req, res) {res.sendFile(__dirname + '/dist/index.html');
});app.listen(8080);
五、如何选择?
-
优先使用 history 模式:
如果项目需要良好的 SEO、更优雅的 URL,且不考虑 IE9 及以下浏览器,推荐使用 history 模式。 -
使用 hash 模式的场景:
- 项目不需要 SEO(如内部管理系统)。
- 无法配置服务器(如 GitHub Pages、静态网站托管)。
- 需要兼容 IE9 及以下浏览器。
六、切换模式的方法
在 Vue Router 配置中,通过 mode
选项指定模式:
const router = new VueRouter({mode: 'history', // 启用 history 模式(默认是 hash 模式)routes: [...]
});
七、总结
场景 | hash 模式 | history 模式 |
---|---|---|
静态网站/快速开发 | ✅ | ❌(需服务器配置) |
企业内部系统 | ✅(兼容性好) | ✅(URL 更优雅) |
电商/内容网站(需 SEO) | ❌ | ✅ |
兼容性要求 | ✅(支持 IE9+) | ❌(不支持 IE9 及以下) |
选择时需权衡 URL 美观性、SEO 需求、服务器配置复杂度和浏览器兼容性。history 模式是现代 Web 应用的首选,但需要服务器配合;hash 模式则适用于快速迭代和兼容性要求高的场景。
(三)、vue-router(路由管理)
vue的一个插件库,专门用来实现SPA应用
1.安装与配置
使用npm install vue-router@3
安装,然后在项目中进行配置。
使用:Vue.use(Router);
// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';Vue.use(Router);export default new Router({// 路由模式,可选'hash'(默认,URL中包含#)和'history'(更美观的URL)mode: 'history', routes: [{// 路由路径path: '/', // 路由名称name: 'Home', // 对应的组件component: Home },{path: '/about',name: 'About',component: About}]
});
在main.js
中引入并使用路由:
import Vue from 'vue';
import App from './App.vue';
import router from './router';new Vue({router,render: h => h(App)
}).$mount('#app');
2.路由导航与参数
- 导航:使用
<router-link>
组件进行路由导航。
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
- 动态路由参数:在路由路径中使用参数,如
/user/:id
,可以在组件中通过$route.params
获取参数值。
// User.vue
export default {created() {const userId = this.$route.params.id;console.log('用户ID:', userId);}
};
(四)对SPA应用的理解
- 定义:单页 Web 应用(single page web application,SPA )
- 页面特征:整个应用只有一个完整的页面
- 交互特点:点击页面中的导航链接不会刷新页面,只会做页面的局部更新
- 数据获取:数据需要通过 ajax 请求获取
(五)基本路由
注意:
- 存储位置
- 路由组件:通常存放在
pages
文件夹 - 一般组件:通常存放在
components
文件夹
- 路由组件:通常存放在
- 路由组件销毁与挂载:通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
- 组件路由信息属性:每个组件都有自己的
$route
属性,里面存储着自己的路由信息 - 全局路由实例获取:整个应用只有一个
router
,可以通过组件的$router
属性获取到 。
路由传参
query参数(静态)
命名路由
一、命名路由的作用
简化路由跳转:无需硬编码完整路径,通过路由名称(name
)实现跳转,避免路径变更时大量修改代码。
二、使用步骤
1. 给路由“命名”(配置阶段)
在路由配置中,为目标路由添加 name
属性:
const routes = [{path: '/demo',component: Demo,children: [{path: 'test',component: Test,children: [{name: 'hello', // 给路由命名(关键)path: 'welcome',component: Hello}]}]}
]
2. 简化跳转(组件中使用)
未简化前:需写完整路径,易出错且难维护:
<router-link to="/demo/test/welcome">跳转</router-link>
简化后:通过 name
跳转,路径变化时只需改配置:
<router-link :to="{ name: 'hello' }">跳转</router-link>
简化写法:命名路由 + query 参数
<!-- 简化写法:命名路由 + query 参数 -->
<router-link :to="{ name: 'hello', // 路由名称(对应路由配置的 name: 'hello')query: { // 传递 query 参数(会显示在 URL 中,如 ?id=666&title=你好)id: 666, title: '你好' } }"
>跳转并传参
</router-link>
三、核心优势
- 解耦路径与跳转:路由名称不变时,即使路径(如
/demo/test/welcome
)修改,跳转代码无需改动。 - 支持动态参数:结合
params
/query
传参更灵活(如:to="{ name: 'hello', params: { id: 1 } }"
)。
总结
命名路由是给路由起一个“别名”(name
),让跳转从“写死路径”变成“调用名称”,核心价值是简化跳转逻辑、提升可维护性。
路由的params参数(动态)
1.配置路由,声明接收 params
参数
配置路由以接收
params
参数,通过 占位符(:id
、:title
) 定义动态路径,让组件能接收 URL
中的动态参数。
1.1. 路由配置(核心片段)
{path: '/home',component: Home,children: [{component: Message,children: [{name: 'xiangqing', path: 'detail/:id/:title', // 占位符声明 params 参数component: Detail}]}]
}
代码解析:
path: 'detail/:id/:title'
:- 用
:id
、:title
占位符,声明该路由需要接收id
和title
两个params
参数。 - 访问时路径如
/home/message/detail/1/xxx
,其中1
和xxx
会作为params
传递。
- 用
流程说明 :
-
路由定义:
通过path: 'detail/:id/:title'
告诉 Vue Router:- 该路径是动态的,
id
和title
是可变参数。 - 这些参数会以
params
形式传递给Detail
组件。
- 该路径是动态的,
-
参数传递与接收:
- 跳转时需通过
params
传入数据(如this.$router.push({ name: 'xiangqing', params: { id: 1, title: 'xxx' } })
)。 Detail
组件中通过this.$route.params.id
、this.$route.params.title
接收参数。
- 跳转时需通过
核心特点
- 动态路径:URL 中的
id
和title
是动态的,不同参数对应不同页面状态。 - 强关联路由:
params
参数必须与路由配置的占位符一一对应,否则路由无法匹配。
1.2对比 query
参数
对比项 | params 参数 | query 参数 |
---|---|---|
路径形式 | /detail/1/xxx (参数嵌入路径) | /detail?id=1&title=xxx (参数跟在 ? 后) |
路由配置 | 需用 :占位符 声明 | 无需特殊配置 |
参数是否必填 | 是(否则路由不匹配) | 否(可传可不传) |
1.3 注意事项
-
参数必填性:
由于params
参数通过占位符声明,跳转时必须传入对应参数,否则路由无法匹配(如少传title
会导致 404)。 -
命名路由配合:
建议结合name: 'xiangqing'
使用,跳转时代码更简洁:this.$router.push({ name: 'xiangqing', params: { id: 1, title: 'xxx' } })
-
组件接收参数:
在Detail
组件中,通过this.$route.params
而非props
接收(除非开启props: true
配置)。
这是 Vue Router 中 动态路由传参 的基础用法,核心是通过 占位符 让 URL 携带动态数据,常用于商品详情、文章详情等场景~
2.params
参数的传递方式:
1. 字符串写法(直接拼接路径)
<!-- 跳转并携带 params 参数(字符串拼接路径) -->
<router-link to="/home/message/detail/666/你好">跳转</router-link>
- 特点:
- 直接拼接
params
到 URL(666
是id
,你好
是title
)。 - 需严格按照路由配置的
path: 'detail/:id/:title'
格式拼接,易出错。
- 直接拼接
2. 对象写法(推荐)
<!-- 跳转并携带 params 参数(对象写法) -->
<router-link :to="{ name: 'xiangqing', params: { id: 666, title: '你好' } }"
>跳转
</router-link>
- 关键规则:
- 必须用
name: 'xiangqing'
(路由的命名),不能用path
。 - 通过
params
字段传递参数,Vue Router 会自动拼接成动态路径。
- 必须用
注意事项
对象写法的强制规则:
- 当用对象传递
params
时,必须通过name
匹配路由,而不能用path
。 - 原因:
path
是静态的,无法动态拼接params
;name
与路由配置的name: 'xiangqing'
关联,能正确解析params
。
两种写法对比
写法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
字符串写法 | 简单直观 | 易因路径变化(如参数顺序)出错 | 简单场景,参数少且固定 |
对象写法 | 解耦路径与参数,更健壮 | 需定义路由 name | 复杂场景,参数多或动态变化 |
核心结论
传递 params
参数时:
- 若用 对象写法,必须配合
name
(不能用path
),这是 Vue Router 的强制规则。 - 推荐优先用 对象写法,避免路径拼接错误,且更易维护(路由名称不变时,路径变化不影响跳转)。
这是 Vue Router 中 params
参数传递的核心细节,记住 “对象写法必须用 name” 即可避免常见错误~
3. 接收 params
参数:
在组件中获取路由传递的 params
参数(如 id
和 title
),用于渲染动态内容(如商品详情、文章标题)。
1.关键代码
// 组件中接收 params 参数
const id = this.$route.params.id
const title = this.$route.params.title
2.流程说明
-
路由传递
params
:
通过router-link
或this.$router.push
传递params
(如{ params: { id: 666, title: '你好' } }
)。 -
组件接收参数:
Vue Router 会将params
挂载到this.$route.params
中,组件直接访问this.$route.params.id
、this.$route.params.title
即可。
3.与 query
参数的区别
参数类型 | 存储位置 | 组件中获取方式 | 特点 |
---|---|---|---|
params | 动态路径(如 /detail/666/你好 ) | this.$route.params.xxx | 参数隐藏在路径中,更“干净” |
query | URL 查询字符串(如 ?id=666 ) | this.$route.query.xxx | 参数显示在 URL 中,可分享 |
4.注意事项
-
路由配置依赖:
必须在路由中通过path: 'detail/:id/:title'
声明params
占位符,否则this.$route.params
无法获取参数。 -
组件更新问题:
如果组件复用(如不同id
但同一路由),需通过watch
监听$route.params
变化:watch: {'$route.params'() {// 参数变化时重新加载数据this.fetchData(this.$route.params.id);} }
5.总结
接收 params
参数的核心是通过 this.$route.params.xxx
,需注意路由配置的占位符声明,以及组件复用场景下的参数监听。这是 Vue Router 动态路由传参的基础用法,常用于商品详情、文章详情等场景。
路由的props配置
在 Vue Router 中,props
配置是一种将路由参数(如 params
、query
)直接映射为组件 props
的机制,目的是解耦组件与路由,让组件更易于测试和复用。以下是其核心用法与场景:
一、基础用法(三种模式)
1. 布尔模式(最常用)
- 配置:
props: true
- 效果:将
params
参数转为组件props
,query
不处理。 - 示例:
// 路由配置 {path: '/user/:id',component: User,props: true // 开启 props 模式 }// User 组件 export default {props: ['id'], // 直接接收路由 params 参数mounted() {console.log(this.id); // 访问路由参数} }
2. 对象模式
- 配置:
props: { key: value }
- 效果:传递固定的静态数据给组件,不依赖路由参数。
- 示例:
{path: '/about',component: About,props: { title: '关于我们', version: '1.0.0' } // 传递静态数据 }
3. 函数模式(最灵活)
- 配置:
props: (route) => ({...})
- 效果:通过函数动态生成
props
,可处理params
、query
或其他逻辑。 - 示例:
{path: '/search',component: Search,props: (route) => ({keyword: route.query.q, // 从 query 获取参数page: parseInt(route.query.page) || 1 // 处理默认值}) }
汇总:
{name:'xiangqing',path:'detail/:id',component:Detail,//第一种写法:props值为对象,该对象中所有的key - value的组合最终都会通过props传给Detail组件// props:{a:900}//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件// props:true//第三种写法:props值为函数,该函数返回的对象中每一组key - value都会通过props传给Detail组件props(route){return {id:route.query.id,title:route.query.title}
二、核心优势
-
组件复用性提升:
组件不再依赖this.$route
,可独立使用(如在非路由场景下复用)。// 无 props 配置时,组件依赖路由 console.log(this.$route.params.id); // 耦合路由// 有 props 配置时,组件只关心 props console.log(this.id); // 解耦路由,可独立测试
-
测试更简单:
测试组件时,可直接传入props
,无需模拟路由环境。// 测试代码示例 const wrapper = shallowMount(User, {propsData: { id: '123' } // 直接传入 props });
-
代码更清晰:
组件props
声明明确,一眼看出依赖哪些参数。
三、对比直接访问 $route
方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
直接用 $route | 简单直接,无需额外配置 | 组件与路由强耦合 | 小型项目、快速开发 |
props 配置 | 解耦路由,组件可复用 | 需要额外配置 | 中大型项目、组件复用多 |
四、注意事项
-
仅处理
params
:
当props: true
时,只有params
参数会被转为props
,query
参数需通过函数模式手动处理。 -
与命名路由配合:
传递params
时,建议用命名路由(如name: 'user'
),避免路径拼接错误。<router-link :to="{ name: 'user', params: { id: 1 } }">用户详情</router-link>
-
函数模式的性能:
函数模式每次路由变化都会执行,避免在函数中做复杂计算,可通过computed
缓存结果。
五、典型场景
-
商品详情页:通过
id
获取商品数据。{path: '/product/:id',component: ProductDetail,props: true }
-
搜索结果页:通过
query
参数获取搜索关键词。{path: '/search',component: SearchResult,props: (route) => ({ keyword: route.query.q }) }
-
静态页面:传递固定数据(如标题、配置)。
{path: '/help',component: HelpPage,props: { title: '帮助中心' } }
总结
props
配置是 Vue Router 中解耦组件与路由的关键机制,通过三种模式(布尔、对象、函数)将路由参数转为组件 props
,让组件更独立、可测试、易复用。建议在中大型项目中优先使用,提升代码质量。
router-link的replace属性
在 Vue Router 中,router-link
是用于生成导航链接的组件,它的 replace
属性主要用于控制导航行为,以下是关于它的详细介绍:
以下是提取的文字内容:
<router-link>
的replace
属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为
push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
- 如何开启
replace
模式:<router-link replace ……>News</router-link>
1. 基本作用
replace
属性的作用是用新的历史记录条目替换当前的历史记录条目,而不是向历史记录栈中添加一个新条目。
在普通情况下,当我们使用 <router-link>
进行页面跳转时,每次跳转都会在浏览器的历史记录中新增一条记录,用户可以通过点击浏览器的“后退”按钮回到上一个页面。但是当给 <router-link>
添加了 replace
属性后,跳转时会替换当前的历史记录,此时浏览器的“后退”按钮将无法退回到跳转前的页面 。
2. 使用方式
在 <router-link>
组件中,直接添加 replace
属性即可,它不需要绑定具体的值,类似于 HTML 中的布尔属性,只要存在该属性,就会生效。示例代码如下:
<template><div><!-- 普通的路由链接,会增加历史记录 --><router-link to="/home">前往首页</router-link> <!-- 使用了replace属性的路由链接,会替换历史记录 --><router-link to="/about" replace>前往关于页面</router-link> </div>
</template>
上述代码中,点击“前往首页”的链接时,浏览器历史记录会新增一条记录;而点击“前往关于页面”的链接时,当前的历史记录会被替换。
3. 应用场景
- 防止用户返回:在一些场景下,比如用户进行了不可逆的操作(如提交表单、支付成功后),不希望用户通过点击“后退”按钮回到之前的页面,此时可以使用
replace
属性。例如,在电商网站中,当用户完成支付后跳转到支付成功页面,为了避免用户误点“后退”按钮回到支付页面造成混乱,可以在支付成功页面的跳转链接中添加replace
属性。
<router-link to="/payment-success" replace>支付成功</router-link>
- 简化历史记录:当页面的导航逻辑比较复杂,且某些跳转不需要用户回溯时,使用
replace
属性可以让浏览器的历史记录更加简洁明了,避免历史记录中出现过多不必要的条目 。
4. 与编程式导航的对比
除了通过 <router-link>
的 replace
属性控制导航行为外,编程式导航(this.$router.push
和 this.$router.replace
)也能实现类似功能。
this.$router.push
:向历史记录栈中添加一个新条目,与普通的<router-link>
效果类似。this.$router.replace
:用新的条目替换当前历史记录条目,类似于<router-link>
加上replace
属性。
示例代码:
export default {methods: {goToPage() {// 普通push,增加历史记录this.$router.push('/some-page'); // 使用replace,替换历史记录this.$router.replace('/another-page'); }}
}
总之,router-link
的 replace
属性为我们在 Vue Router 中控制导航的历史记录行为提供了一种便捷的方式,合理使用它可以优化用户体验和页面导航逻辑。
编程式路由导航
在 Vue Router 中,除了使用 <router-link>
声明式创建导航链接外,还可以通过 编程式路由导航 实现更灵活的路由跳转。这种方式通过 JavaScript 代码触发路由变化,适用于条件跳转、事件回调等场景。
一、核心 API
1. this.$router.push(location)
- 作用:向历史记录栈中添加一个新条目,等同于
<router-link to>
。 - 参数:可以是字符串路径或描述目标位置的对象。
- 示例:
// 字符串路径 this.$router.push('/home');// 对象路径(命名路由 + params) this.$router.push({ name: 'user', params: { id: 123 } });// 对象路径(带 query 参数) this.$router.push({ path: '/search', query: { keyword: 'vue' } });
2. this.$router.replace(location)
- 作用:替换当前历史记录条目,不创建新条目,等同于
<router-link replace>
。 - 示例:
// 替换当前路由,禁止返回上一页 this.$router.replace('/login');
3. this.$router.go(n)
- 作用:在历史记录中前进或后退,参数
n
为步数。 - 示例:
this.$router.go(1); // 前进一页(等同于 history.forward()) this.$router.go(-1); // 后退一页(等同于 history.back()) this.$router.go(3); // 前进三页
二、与 <router-link>
的对比
方式 | 适用场景 | 特点 |
---|---|---|
<router-link> | 静态导航链接(如导航栏、菜单) | 声明式,简单直观 |
编程式路由导航 | 动态逻辑跳转(如按钮点击、条件判断) | 灵活,可结合业务逻辑,支持异步操作 |
三、典型场景
1. 按钮点击跳转
<template><button @click="handleLogin">登录</button>
</template><script>
export default {methods: {handleLogin() {// 登录逻辑...this.$router.push('/dashboard');}}
}
</script>
2. 条件跳转
// 根据用户权限跳转
if (this.isAdmin) {this.$router.push('/admin');
} else {this.$router.push('/user');
}
3. 异步操作后跳转
async fetchData() {await this.api.getUser();this.$router.replace('/profile'); // 替换路由,禁止返回
}
4. 带参数跳转
// 传递 params 参数
this.$router.push({ name: 'product', params: { id: this.selectedId }
});// 传递 query 参数
this.$router.push({ path: '/search', query: { page: 2 }
});
四、注意事项
-
路由钩子与导航守卫:
编程式导航同样会触发全局导航守卫(如beforeEach
)和路由独享守卫(如beforeEnter
),需注意逻辑顺序。 -
命名路由优先:
传递动态参数(如params
)时,建议使用命名路由,避免路径拼接错误。 -
错误处理:
导航失败(如路由不存在)会返回一个被拒绝的 Promise,可以通过.catch
捕获错误:this.$router.push('/non-existent').catch(err => {console.error('导航失败:', err); });
五、总结
编程式路由导航是 Vue Router 提供的 JavaScript 方式控制路由跳转 的能力,通过 push
、replace
、go
等方法实现灵活的导航逻辑,适用于需要动态控制路由的场景。与 <router-link>
结合使用,能满足各种复杂的路由需求。
缓存路由组件
在 Vue Router 中,缓存路由组件 是提升单页应用性能和用户体验的重要手段,主要通过 <keep-alive>
组件实现。以下是核心机制与使用场景:
一、核心原理
- 普通组件行为:组件在切换时会被销毁(
beforeDestroy
/destroyed
),再次进入时重新创建(created
/mounted
)。 - 缓存组件行为:使用
<keep-alive>
包裹的组件,在切换时不会被销毁,而是进入“缓存状态”(deactivated
),再次进入时直接恢复(activated
),避免重复渲染和数据请求。
二、基础用法(✔️)
1. 全局缓存所有路由组件
<!-- App.vue -->
<router-view v-slot="{ Component }"><keep-alive><component :is="Component" /></keep-alive>
</router-view>
2. 缓存特定组件(推荐)
通过 include
/exclude
指定缓存组件名称(组件的 name
选项):
<!-- 只缓存 Home 和 User 组件 -->
<keep-alive include="Home,User">//include:组件名<router-view />
</keep-alive><!-- 排除 Login 组件 -->
<keep-alive exclude="Login"><router-view />
</keep-alive>
3. 动态缓存(通过路由元信息)
在路由配置中添加 meta.keepAlive
,动态控制是否缓存:
// router.js
const routes = [{path: '/home',component: Home,meta: { keepAlive: true } // 需要缓存},{path: '/detail',component: Detail,meta: { keepAlive: false } // 不需要缓存}
];
<!-- App.vue -->
<router-view v-slot="{ Component }"><keep-alive><component :is="Component" v-if="$route.meta.keepAlive" /></keep-alive><component :is="Component" v-if="!$route.meta.keepAlive" />
</router-view>
语法与作用说明
在 Vue 中,
<keep-alive>
组件的include
属性用于指定需要缓存的组件名称,值可以是字符串、正则表达式,也可以是数组(数组里放组件名称字符串) 。你代码里的写法:<keep-alive :include="['News','Message']"> <router-view></router-view> </keep-alive>
表示:仅缓存名称为
News
和Message
的组件,当这两个组件在<router-view>
中切换时,会被缓存(组件实例不会销毁,再次进入时不会重新执行创建阶段的钩子,而是触发activated
等缓存相关钩子 ),其他未匹配名称的组件则不会被缓存。关键前提条件
- 组件需有
name
选项:News
和Message
组件必须在自身定义中显式声明name
,且名称要和include
里的字符串严格一致,
示例:// News.vue export default { name: 'News', // 必须声明,且和 include 里的 'News' 一致 // ... }// Message.vue export default { name: 'Message', // ... }
如果是单文件组件未手动写
name
,Vue 会默认将文件名作为name
(比如News.vue
会默认name: 'News'
),但为了清晰和避免意外,建议手动声明。
- 配合路由使用场景: 这种写法通常用于在路由切换时,缓存特定的页面级组件(即通过
router-view
渲染的组件 )。比如News
和Message
是两个路由组件,通过路由配置映射到不同路径,当在这两个路由间切换时,就能利用
keep-alive
缓存它们的状态。常见扩展用法对比
- 字符串形式:
include="News"
,只能缓存一个组件,适合单一目标场景。- 正则形式:
:include="/News|Message/"
(需结合v-bind
绑定,因为是 JavaScript 表达式 ),用正则匹配组件名称,灵活但相对难维护。- 数组形式(你代码里的写法):
:include="['News','Message']"
,清晰列出要缓存的组件,适合明确知道缓存范围的场景,也是实际项目里常用的方式。只要满足上述组件
name
匹配等条件,你的写法是合法且推荐的,能精准控制缓存范围,避免不必要的组件缓存,节省内存、提升性能。
核心区别:静态字符串 vs 动态绑定数组
写法 | 语法类型 | 解析方式 |
---|---|---|
include="Home,User,Product" | 静态字符串 | Vue 会直接将字符串按逗号分割为数组 ['Home', 'User', 'Product'] |
:include="['Home','User']" | 动态绑定 JavaScript 数组 | 通过 v-bind 绑定 JS 表达式,需在 Vue 实例中定义或直接写数组字面量 |
三、缓存组件的生命周期钩子
activated
:组件被激活时触发(首次加载或从缓存中恢复)。deactivated
:组件被缓存时触发(离开但不销毁)。
典型应用:在 activated
中判断是否需要重新加载数据:
export default {data() {return {data: null,loaded: false};},activated() {if (!this.loaded) {this.fetchData(); // 只在首次加载或数据过期时请求this.loaded = true;}},methods: {fetchData() {// API 请求...}}
};
四、适用场景
- 频繁切换的页面:如标签页(Tab),避免每次切换都重新渲染。
- 数据加载耗时的页面:如商品详情页,缓存后二次访问无需重新请求数据。
- 需要保存状态的表单:如多步骤表单,切换步骤时保留已填写内容。
五、注意事项
- 内存占用:过多缓存组件会增加内存开销,建议仅缓存关键页面。
- 数据更新:缓存组件不会触发
mounted
,需在activated
中处理数据更新。 - 组件复用问题:动态路由(如
/user/:id
)切换不同参数时,组件会复用而不触发activated
,需通过watch
监听$route
变化:watch: {$route() {this.fetchData(); // 路由参数变化时重新加载} }
六、高级用法
1. 结合 max
属性限制缓存数量
<keep-alive max="3"> <!-- 最多缓存 3 个组件 --><router-view />
</keep-alive>
2. 动态控制缓存状态
通过修改路由元信息或组件 name
,在运行时动态决定是否缓存:
// 导航守卫中动态设置
router.beforeEach((to, from, next) => {if (to.name === 'Detail' && from.name === 'List') {to.meta.keepAlive = true; // 从列表页进入详情页时缓存}next();
});
七、总结
缓存路由组件是优化 SPA 性能的重要手段,通过 <keep-alive>
实现组件复用,避免重复渲染和数据请求。合理使用 include
/exclude
、路由元信息及生命周期钩子,能在提升体验的同时避免内存问题。
案例
以下是关于 Vue Router 核心特性的综合解析,通过 电商商品详情页 案例串联所有知识点:
案例场景
假设你正在开发一个电商网站,需要实现以下功能:
- 从商品列表页点击商品,跳转到详情页(携带商品 ID)。
- 详情页包含 基本信息、用户评价、相关推荐 三个子页面(嵌套路由)。
- 评价页支持按时间/评分筛选(query 参数)。
- 商品详情页需要缓存,避免重复加载数据。
1. 路由配置(router.js
)
const routes = [// 商品列表页{ path: '/products', component: ProductsList },// 商品详情页(动态路由 + 嵌套路由){path: '/product/:id', // params 参数:商品 IDname: 'ProductDetail', // 命名路由component: ProductDetail,props: true, // 启用 props 接收 paramschildren: [{ path: '', redirect: 'info' }, // 默认子路由{ path: 'info', component: ProductInfo },{ path: 'reviews', component: ProductReviews },{ path: 'related', component: RelatedProducts }]}
];
2. 路由传参与导航
(1)query 参数(筛选条件)
<!-- 商品评价页:筛选按钮 -->
<router-link :to="{ name: 'ProductDetail', params: { id: productId }, query: { sort: 'time' } }"
>按时间排序
</router-link><!-- 在组件中获取 query 参数 -->
export default {computed: {sortType() {return this.$route.query.sort || 'default';}}
}
(2)命名路由 + params 参数(商品 ID)
<!-- 商品列表项:跳转到详情页 -->
<router-link :to="{ name: 'ProductDetail', params: { id: item.id } }"
>{{ item.name }}
</router-link><!-- 编程式导航(按钮点击) -->
methods: {goToDetail() {this.$router.push({name: 'ProductDetail',params: { id: this.selectedId }});}
}
(3)路由的 props 配置(简化参数接收)
// 路由配置
{path: '/product/:id',component: ProductDetail,props: true // 将 params 转为组件 props
}// 在 ProductDetail 组件中直接接收
export default {props: ['id'],mounted() {this.fetchProductData(this.id);}
}
3. router-link 的 replace 属性
<!-- 使用 replace 替换当前历史记录,禁止后退 -->
<router-link :to="{ name: 'ProductDetail', params: { id } }"replace
>立即购买
</router-link>
4. 缓存路由组件(keep-alive
)
<!-- App.vue:缓存商品详情页 -->
<router-view v-slot="{ Component }"><keep-alive include="ProductDetail"><component :is="Component" /></keep-alive>
</router-view><!-- 被缓存组件的生命周期钩子 -->
export default {activated() {// 组件被激活时触发(缓存后首次加载或重新显示)if (!this.$route.meta.loaded) {this.fetchData(); // 只在首次加载时请求数据this.$route.meta.loaded = true;}},deactivated() {// 组件被缓存时触发(隐藏但不销毁)}
}
5. 完整流程图
商品列表页(/products)└── 点击商品(携带 id 参数)↓
商品详情页(/product/123)├── 默认显示:基本信息(/product/123/info)├── 切换到评价页(/product/123/reviews?sort=time)│ └── query 参数控制筛选逻辑└── 切换到相关推荐(/product/123/related)[缓存机制]
当从详情页跳转到其他页面时,详情页组件不会销毁,
再次返回时直接显示缓存内容,无需重新加载数据。
核心知识点总结
特性 | 作用 | 示例 |
---|---|---|
query 参数 | 用于传递可选参数(如筛选条件),显示在 URL 中(?sort=time )。 | <router-link :to="{ query: { sort: 'time' } }"> |
命名路由 | 为路由定义名称,避免硬编码路径,提高可维护性。 | name: 'ProductDetail' + this.$router.push({ name: '...' }) |
params 参数 | 用于传递必需参数(如 ID),通过动态路由(:id )接收。 | path: '/product/:id' + this.$route.params.id |
props 配置 | 将 params 转为组件 props,解耦组件与路由。 | props: true + export default { props: ['id'] } |
replace 属性 | 替换当前历史记录,禁止返回上一页。 | <router-link replace> |
缓存路由组件 | 使用 keep-alive 缓存组件状态,避免重复渲染。 | <keep-alive include="ProductDetail"> + activated() 生命周期钩子 |
两个新的生命周期钩子
- ** 作用**:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
- 具体名字:
activated
路由组件被激活时触发。deactivated
路由组件失活时触发。
Vue UI组件库
一、移动端组件库
库名 | 特点 | 技术栈 | 官网地址 | 适用场景 |
---|---|---|---|---|
Vant | 有赞出品,更新活跃,组件全 | Vue 2/3 | https://vant-contrib.gitee.io/vant/ | 电商、表单、中大型项目 |
NutUI | 京东出品,轻量高效 | Vue 2/3 | https://nutui.jd.com/ | 快速迭代项目 |
Cube UI | 滴滴开源,定制性强 | Vue 2 | https://didi.github.io/cube-ui/ | 需深度定制的移动端项目 |
Mint UI | 经典老库(维护停滞) | Vue 2 | https://github.com/ElemeFE/mint-ui | 旧项目维护(慎用于新项目) |
二、PC 端组件库
库名 | 特点 | 技术栈 | 官网地址 | 适用场景 |
---|---|---|---|---|
Element UI | 元老级,文档完善 | Vue 2 | https://element.eleme.cn/ | 企业后台、管理系统 |
Element Plus | Element 官方 Vue 3 版本 | Vue 3 | https://element-plus.org/ | 新项目(Vue 3 技术栈) |
View UI | 原 iView,设计现代 | Vue 2/3 | https://www.iviewui.com/ | 中后台系统、数据可视化 |
Ant Design Vue | 阿里系,组件丰富 | Vue 2/3 | https://www.antdv.com/ | 复杂业务系统、中台产品 |
三、跨端/全场景组件库
库名 | 特点 | 技术栈 | 官网地址 | 适用场景 |
---|---|---|---|---|
Naive UI | 全场景,性能优异 | Vue 3 | https://www.naiveui.com/ | 中大型 Web 应用(Vue 3) |
Arco Design | 字节跳动出品,设计统一 | Vue 2/3 | https://arco.design/vue | 企业级产品、多端一致性需求 |
四、使用建议
-
技术栈匹配:
- Vue 2:选 Element UI、Vant、View UI
- Vue 3:选 Element Plus、Naive UI、Ant Design Vue
-
场景选择:
- 电商/移动端:Vant(功能最全)
- 企业后台:Element UI/Plus(稳定)或 Ant Design Vue(组件丰富)
- 快速开发:NutUI(体积小)或 View UI(设计现代)
-
避坑指南:
- 避免在新项目中使用 Mint UI(已停止维护)
- 注意组件库与 Vue 版本的兼容性
- 按需引入组件以减小打包体积(参考各库文档)
五、快速上手示例(Vant)
# 安装
npm i vant@next -S # Vue 3 项目
npm i vant -S # Vue 2 项目# 按需引入(推荐)
npm i unplugin-vue-components unplugin-auto-import -D
// vite.config.js
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'export default defineConfig({plugins: [AutoImport({resolvers: [VantResolver()],}),Components({resolvers: [VantResolver()],}),],
})