vue3超详细教学(傻瓜版)百万字超详细-路由
一、路由到底是个啥?(基础概念篇)
1. 路由(Router)
- 梗版:你就把路由当成小区门口的保安大爷 —— 你报个门牌号(URL),大爷就告诉你该往哪栋楼走(加载哪个页面),绝对不会让你走错门。要是大爷不在(没路由),你就得在小区里瞎转悠(页面乱跳)。
- 专业版:路由是 URL 与页面 / 组件之间的映射关系。当用户访问不同 URL 时,路由会根据预设的映射规则,自动加载对应的页面或组件,实现无刷新的页面切换。
2. 单页面应用(SPA)
- 梗版:就像一个大火锅 —— 只有一个锅底(主页面),想吃毛肚(首页)、肥牛(详情页)、虾滑(设置页),直接往里下就行,不用换锅底(不刷新页面),效率高还省事儿!
- 专业版:单页面应用是指整个应用只有一个主 HTML 页面,所有内容都在这个页面内动态加载和渲染。路由通过 JavaScript 实现页面内容的切换,避免了传统多页面应用的频繁刷新,提升用户体验。
二、路由模式:三种 "指路牌" 的区别
1. hash 模式(默认)
- 梗版:URL 里带个 "#",像给指路牌贴了块胶布 —— 比如
http://xxx.com/#/home。优点是胶布粘性好(兼容性强,IE 都认识),缺点是不好看,像个补丁。而且不管你怎么撕胶布(改 后面的内容),门(页面)都不会重新开(不刷新)。 - 专业版:基于 URL 的 hash 值(# 后面的部分)实现,通过
hashchange事件监听变化。优点是兼容性极好,无需服务器配置;缺点是 URL 含 不够美观,不利于 SEO。适用于中小型应用。
2. history 模式
- 梗版:URL 干干净净,像高档小区的金属指路牌 ——
http://xxx.com/home,看着就舒服。但这牌子娇贵(需要服务器配置),要是配置不好,客人一回头(刷新页面)就找不着门(404 错误)。 - 专业版:基于 HTML5 History API 实现,URL 无 更美观且利于 SEO。但需要服务器支持(所有路径指向 index.html),否则刷新会 404。适用于对 URL 美观和 SEO 有要求的应用(如企业官网、电商)。
3. abstract 模式
- 梗版:这是给 "内部员工" 用的指路牌 —— 不在小区门口(非浏览器环境),比如在后台仓库(Node.js)、小程序里才用,普通业主(用户)看不到。
- 专业版:不依赖浏览器环境,路由状态存于内存。适用于服务端渲染(SSR)、Weex 等非浏览器场景,一般 Web 应用用不到。
代码示例(基础路由配置)
▼typescript复制代码// router/index.ts import { createRouter, createWebHistory } from 'vue-router' // 导入页面组件 import Home from '../views/Home.vue' import User from '../views/User.vue' // 路由规则:URL和组件的对应关系 const routes = [ { path: '/home', // 访问路径 name: 'Home', // 路由名称(可选,方便编程式导航) component: Home // 对应组件 }, { path: '/user', name: 'User', component: User } ] // 创建路由实例 const router = createRouter({ history: createWebHistory(), // 路由模式(history模式) routes // 应用路由规则 }) export default router
三、动态路由:一个页面搞定 N 种内容
代码解析
-
path: '/home':- 梗版:相当于 "小区 3 号楼" 的门牌号,用户按这个地址找,准能到家门口。
- 专业版:定义浏览器访问的路径,是路由匹配的核心依据。
-
component: Home:- 梗版:门牌号对应的 "住户",3 号楼住着 "Home 一家",访问 3 号楼就找这家人。
- 专业版:指定该路由匹配成功后要渲染的 Vue 组件。
-
路由模式:
- 梗版:
createWebHistory()是 "高档小区无遮挡门牌号"(URL 无 #);createWebHashHistory()是 "贴了胶布的门牌号"(URL 带 #)。 - 专业版:
createWebHistory基于 HTML5 History API,createWebHashHistory基于 URL hash,前者更美观但需服务器配置。
- 梗版:
1. 为啥需要动态路由?
- 梗版:你逛 CSDN 时,所有博客页面长得都差不多(标题、内容区、评论区),就内容不一样。总不能每篇博客都单独写个页面吧?那程序员得累死!动态路由就是 "一鱼多吃"—— 一个页面模板,换不同内容。
- 专业版:当多个 URL 需要匹配同一个组件(仅内容不同)时,动态路由可通过 URL 参数区分内容,避免重复开发组件。如博客详情页
/blog/1、/blog/2,共用一个 Blog 组件,通过 id 参数加载不同内容。
2. 动态参数怎么写?
- 梗版:在路径里加个
:参数名,像给抽屉贴标签 ——/user/:id,访问/user/123就打开 "123 号抽屉",访问/user/456就打开 "456 号抽屉",抽屉样式(组件)一样,里面的东西(数据)不同。 - 专业版:通过
:参数名定义动态部分,如path: '/user/:id'。访问/user/123时,参数id的值为 '123',可通过route.params.id获取,实现组件复用。
3. 参数还能搞限制?
- 梗版:你可以规定 "抽屉只能放数字"——
/user/:id(\\d+),要是有人往抽屉里塞字母(访问/user/abc),就打不开(不匹配路由),防止瞎捣乱! - 专业版:支持正则约束参数格式,
\\d+表示仅匹配数字;?表示可选参数(/user/:id?匹配/user或/user/123);*表示通配符(/user/*匹配/user/a/b)。
2. 动态路由代码实现
▼typescript复制代码// 路由配置(快递柜规则) const routes = [ { path: '/blog/:blogId', // :blogId是快递柜编号(动态参数) name: 'BlogDetail', component: () => import('../views/BlogDetail.vue') // 快递柜(组件) } ] // 组件中取参数(BlogDetail.vue) <script setup> import { useRoute } from 'vue-router' const route = useRoute() // 拿取件码(获取路由信息) // 访问 /blog/123 时,输出"当前博客ID:123" console.log('当前博客ID:', route.params.blogId) </script>
3. 代码逐行解析
-
path: '/blog/:blogId':- 梗版:
:blogId是快递柜的编号(1、2、3...),不管你存 1 号柜还是 2 号柜,柜子本身(BlogDetail 组件)是同一个,只是里面的快递(博客内容)不同。 - 专业版:通过
:参数名定义 URL 中的动态部分,blogId为参数名,访问/blog/123时,参数值为 '123',存储在route.params.blogId中。 多参数与约束示例
- 梗版:
▼typescript复制代码// 多参数:快递柜+抽屉 path: '/user/:userId/post/:postId' // 匹配 /user/123/post/456 → params: { userId: '123', postId: '456' } // 参数约束:只允许数字编号 path: '/user/:userId(\\d+)' // 只匹配 /user/123,不匹配 /user/abc(字母不行) // 可选参数:可填可不填的编号 path: '/search/:keyword?' // 匹配 /search 或 /search/vue(keyword可选)
-
- 梗版:多参数就像 "快递柜 + 抽屉",一层套一层;约束参数就像 "只收正方形快递",圆形的不收;可选参数就像 "快递柜可匿名",填不填编号都行。
-
专业版:支持多参数、正则约束(如
\\d+限制数字)、可选参数(?标记),提升路由匹配的精准性。 -
useRoute()获取参数: -
梗版:相当于快递柜的 "取件码生成器",调用它就能拿到
route对象,里面藏着blogId这样的取件码,凭码才能取快递(获取数据)。 -
专业版:组合式 API 中获取当前路由信息的钩子函数,返回路由实例,通过
route.params访问动态参数,替代 Vue2 中的this.$route。
还是看不懂请看这里
一、先搞懂核心概念:路由参数
你先想一个场景:
我们有个博客网站,每篇博客都有唯一 ID(比如 123、456),对应的 URL 可能是 http://xxx.com/blog/123、http://xxx.com/blog/456。
这里的 123、456 就是 路由参数(动态变化的部分),我们需要在组件里拿到这个参数,才能显示对应 ID 的博客内容 —— 而这段代码的作用,就是拿这个参数。
二、逐行拆解代码
1. 导入路由工具 useRoute
▼javascript复制代码import { useRoute } from 'vue-router'
vue-router是 Vue 官方的路由库,用来管理页面跳转和 URL 配置;useRoute是 Vue Router 提供的「组合式 API」(专门给<script setup>用的),作用是「获取当前页面的路由信息对象」(比如 URL、参数、路径等);- 类比:就像你要拿快递,先从快递柜工具包(
vue-router)里拿出「取件码工具」(useRoute)。
2. 调用 useRoute 获取路由实例
▼javascript复制代码const route = useRoute() // 拿取件码(获取路由信息)
- 调用
useRoute()后,会返回一个「路由信息对象」,我们把它存到route变量里; - 这个
route对象里包含了当前 URL 的所有信息:比如路径(path)、查询参数(query)、动态参数(params)等; - 注释里的「拿取件码」是比喻:
route就像快递的「取件码纸条」,上面写着你需要的「动态参数」(比如博客 ID)。
3. 从路由参数中提取博客 ID 并打印
▼javascript复制代码console.log('当前博客ID:', route.params.blogId)
-
关键是
route.params:params是route对象的一个属性,专门存储「URL 中动态定义的参数」(比如blogId); -
为什么是
blogId?这和你的 路由规则配置 有关!比如在 Vue Router 的路由表中,你肯定定义了这样一条路由:
▼javascript复制代码// 路由表示例(通常在 router/index.js 中) const routes = [ { path: '/blog/:blogId', // 这里的 :blogId 就是动态参数(冒号开头) component: BlogDetail // 对应的博客详情组件(就是当前写这段代码的组件) } ]
-
- 路由规则里的
:blogId是「参数名」,告诉 Vue Router:/blog/后面的内容是动态参数,名字叫blogId;- 当访问
http://xxx.com/blog/123时,Vue Router 会自动把123存到route.params.blogId里;
- 当访问
- 路由规则里的
- 最终效果:访问
/blog/123时,控制台会输出当前博客ID:123,访问/blog/456时输出当前博客ID:456。
三、嵌套路由:"俄罗斯套娃" 式组件结构
1. 嵌套路由是什么?
- 梗版:就像俄罗斯套娃 —— 大娃(父路由
/user)里套着二娃(子路由/user/profile)和三娃(子路由/user/settings)。大娃的壳(父组件布局)不变,里面的小娃(子组件内容)可以换。比如用户中心页面,侧边栏(父组件)不变,点不同菜单切换主内容(子组件)。 - 专业版:在父路由组件内部嵌套其他路由组件,通过
children属性定义子路由,子组件渲染在父组件的<router-view />中,实现 "布局不变,内容切换"。
2. 嵌套路由代码实现
▼typescript复制代码// 路由配置(套娃规则) const routes = [ { path: '/user', // 大娃(父路由) component: UserLayout, // 大娃的壳(父组件) children: [ { path: 'profile', // 二娃(子路由,完整路径:/user/profile) component: UserProfile // 二娃的内容 }, { path: 'settings', // 三娃(子路由,完整路径:/user/settings) component: UserSettings // 三娃的内容 } ] } ] // 父组件 UserLayout.vue(大娃的壳) <template> <div class="user-container"> <!-- 固定的侧边栏(大娃的胳膊腿) --> <aside> <router-link to="/user/profile">个人资料</router-link> <router-link to="/user/settings">设置</router-link> </aside> <!-- 子组件显示区(大娃的肚子,放小娃) --> <main> <router-view /> <!-- 子路由占位符 --> </main> </div> </template>
3. 代码逐行解析
-
children属性:- 梗版:父组件的 "抽屉列表",
profile和settings是两个抽屉,点哪个抽屉,就从<router-view />这个窗口里显示哪个抽屉的内容。 - 专业版:定义子路由列表,子路由路径为相对路径(无需加 /),完整路径为父路径 + 子路径(如
/user+profile=/user/profile)。
- 梗版:父组件的 "抽屉列表",
-
<router-view />标签:- 梗版:父组件上的 "小窗口",子组件就从这个窗口钻出来,窗口位置固定,里面的内容可以换(切换子路由时)。没有这个窗口,子组件就没地方显示了。
- 专业版:子路由组件的占位符,父组件渲染时,匹配的子路由组件会替换到该位置,是实现嵌套路由的核心标签。
四、路由懒加载:"外卖点单" 式加载组件
1. 为什么需要懒加载?
- 梗版:非懒加载就像 "办宴席"—— 客人还没到,所有菜全做好端上桌(应用启动就加载所有组件),菜凉了(资源浪费),客人还得等半天(首屏加载慢)。懒加载像 "外卖点单"—— 客人点一个菜做一个(访问路由时加载组件),新鲜又高效!
- 专业版:传统路由在应用初始化时加载所有组件,导致首屏加载时间长、包体积大。懒加载通过动态导入语法,仅在用户访问路由时才加载对应组件,减少初始加载资源,提升应用启动速度。
2. 懒加载代码实现
▼typescript复制代码// 非懒加载(办宴席模式,不推荐) import Home from '../views/Home.vue' const routes = [{ path: '/home', component: Home }] // 懒加载(外卖点单模式,推荐) const routes = [ { path: '/home', // 访问 /home 时才加载 Home 组件(点单后才做菜) component: () => import('../views/Home.vue') }, { path: '/about', component: () => import('../views/About.vue') // 按需加载 } ]
3. 代码解析
component: () => import('../views/Home.vue'):- 梗版:给 Home 组件挂了个 "按需启动" 的牌子,平时歇着(不加载),用户访问
/home时才开工(加载),不浪费服务器资源。 - 专业版:使用 ES6 动态导入函数,返回一个 Promise。当路由被访问时,Vue Router 会解析 Promise 并加载组件,实现按需加载,减少首屏加载时间。
- 梗版:给 Home 组件挂了个 "按需启动" 的牌子,平时歇着(不加载),用户访问
为什么这样就能按需启动了
component: () => import('../views/Home.vue') 这行代码,就相当于给路由订了个 “外卖”:
component是 “餐桌”,专门放要吃的 “菜”(组件);() => ...是个 “跑腿小哥”,但他特懒,不喊他不动;import('../views/Home.vue')是 “后厨”,藏着Home.vue这道菜的原材料。
平时这小哥就蹲在旁边玩手机,只有当你点了 “Home 套餐”(访问 /home 路由),他才慢悠悠跑去后厨叫人做菜(加载组件)。要是你从不去 /home,这菜就永远躺在后厨,不占你餐桌空间(浏览器内存)—— 这不就是 “按需启动” 嘛!跟你刷视频不点不加载广告一个道理,主打一个 “不浪费”!
专业版讲解:动态导入与路由懒加载的机制
-
component属性的作用:在 Vue Router 中,
component用于指定路由匹配时要渲染的组件。它可以直接接收一个组件对象(如component: Home),也可以接收一个 “返回组件的函数”。 -
箭头函数
() => ...的意义:这里的箭头函数是一个 “延迟执行器”。它本身不执行任何操作,只是定义了一个 “如何获取组件” 的逻辑。Vue Router 会在路由首次被访问时,才调用这个函数,触发后续的组件加载。
-
动态导入
import(...)的特性:- 这是 ES6 引入的动态导入语法,与静态
import Home from '...'不同:- 静态导入在代码解析阶段就会加载文件,无论是否使用;
- 动态
import(...)是 “运行时加载”,只有当函数被调用时,才会发送请求加载目标文件(../views/Home.vue)。
- 这是 ES6 引入的动态导入语法,与静态
-
“按需启动” 的核心逻辑:
当路由配置为
component: () => import(...)时:- 应用初始化时,不会加载
Home.vue的代码; - 当用户首次访问该路由(如点击
/home链接),Vue Router 调用箭头函数,触发import(...); - 浏览器加载
Home.vue并编译为组件,随后渲染到页面; - 未访问时不加载,实现 “按需加载”,减少首屏加载时间。
- 应用初始化时,不会加载
五、导航守卫:路由切换的 "安检员"
1. 导航守卫是什么?
- 梗版:就像小区大门的保安 —— 你进任何楼(切换路由)前,保安都要查通行证(验证身份)。没证?去物业办(重定向到登录页);有证?放行!不同的守卫就像不同岗位的保安,有的查进门,有的查出门,有的查特定楼栋。
- 专业版:在路由切换的不同阶段执行的钩子函数,用于身份验证、权限检查、日志记录等。Vue Router 提供全局守卫、路由独享守卫、组件内守卫等多种类型。
2. 全局前置守卫代码实现
▼typescript复制代码// router/index.ts const router = createRouter({ ... }) // 全局前置守卫:所有路由切换前都要过这关(大门保安) router.beforeEach((to, from, next) => { // to:要去的路由(目标楼) // from:当前所在路由(当前楼) // next:是否放行的按钮(必须调用) // 判断目标路由是否需要登录(贴了"需通行证"标签) if (to.meta.requiresAuth) { // 检查是否有通行证(token) if (localStorage.getItem('token')) { next() // 有证,放行 } else { next('/login') // 无证,.redirect到物业(登录页) } } else { next() // 不需要证,直接放行 } }) // 给需要登录的路由贴标签 const routes = [ { path: '/admin', component: Admin, meta: { requiresAuth: true } // 标记:需要登录 } ]
3. 其他守卫示例
- 全局后置钩子(afterEach):
▼typescript复制代码// 路由切换后执行(保安登记来访记录) router.afterEach((to, from) => { console.log(`用户从${from.path}跳到了${to.path}`) // 记录日志 })
-
- 梗版:客人进楼后,保安在本子上记一笔 "谁从哪来,到哪去",不拦着,只记录。
- 专业版:导航完成后触发,无
next函数,不能改变导航,常用于记录访问日志、统计页面浏览量。 - 路由独享守卫(beforeEnter):
▼typescript复制代码const routes = [ { path: '/admin', component: Admin, // 只看守这个路由(特定楼栋的保安) beforeEnter: (to, from, next) => { if (isAdmin()) { // 检查是否是管理员 next() } else { next('/forbidden') // 不是管理员,去禁止访问页 } } } ]
-
- 梗版: admin 楼自己的保安,只查进这栋楼的人,不是管理员不让进,其他楼不管。
- 专业版:定义在单个路由配置中的守卫,仅在访问该路由时触发,用于特定路由的权限控制。
练习题(检验学习成果)
- 题目:配置一个动态路由
/product/:productId,要求 productId 只能是数字,在组件中获取该参数并打印。
▼typescript复制代码// 路由配置 const routes = [ { path: '/product/:productId(\\d+)', // 仅允许数字 component: () => import('../views/Product.vue') } ] // Product.vue <script setup> import { useRoute } from 'vue-router' const route = useRoute() console.log('商品ID:', route.params.productId) // 访问/product/100 → 输出100 </script>
题目:实现嵌套路由/shop,包含子路由/shop/goods和/shop/cart,父组件显示导航栏,子组件在导航栏下方显示。
▼typescript复制代码// 路由配置 const routes = [ { path: '/shop', component: ShopLayout, children: [ { path: 'goods', component: ShopGoods }, { path: 'cart', component: ShopCart } ] } ] // ShopLayout.vue <template> <div> <nav> <router-link to="/shop/goods">商品列表</router-link> <router-link to="/shop/cart">购物车</router-link> </nav> <router-view /> <!-- 子组件显示区 --> </div> </template>
这样知识点、代码、搞笑解读全结合,是不是既学得懂又记得牢?路由这东西看着复杂,其实就是 "保安 + 快递柜 + 套娃 + 外卖" 的组合,多写几个例子就熟啦!

