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(比如 123456),对应的 URL 可能是 http://xxx.com/blog/123http://xxx.com/blog/456

这里的 123456 就是 路由参数(动态变化的部分),我们需要在组件里拿到这个参数,才能显示对应 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属性:

    • 梗版:父组件的 "抽屉列表",profilesettings是两个抽屉,点哪个抽屉,就从<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 并加载组件,实现按需加载,减少首屏加载时间。

为什么这样就能按需启动了

component: () => import('../views/Home.vue') 这行代码,就相当于给路由订了个 “外卖”:

  • component 是 “餐桌”,专门放要吃的 “菜”(组件);
  • () => ... 是个 “跑腿小哥”,但他特懒,不喊他不动;
  • import('../views/Home.vue') 是 “后厨”,藏着 Home.vue 这道菜的原材料。

平时这小哥就蹲在旁边玩手机,只有当你点了 “Home 套餐”(访问 /home 路由),他才慢悠悠跑去后厨叫人做菜(加载组件)。要是你从不去 /home,这菜就永远躺在后厨,不占你餐桌空间(浏览器内存)—— 这不就是 “按需启动” 嘛!跟你刷视频不点不加载广告一个道理,主打一个 “不浪费”!

专业版讲解:动态导入与路由懒加载的机制
  1. component 属性的作用

    在 Vue Router 中,component 用于指定路由匹配时要渲染的组件。它可以直接接收一个组件对象(如 component: Home),也可以接收一个 “返回组件的函数”。

  2. 箭头函数 () => ... 的意义

    这里的箭头函数是一个 “延迟执行器”。它本身不执行任何操作,只是定义了一个 “如何获取组件” 的逻辑。Vue Router 会在路由首次被访问时,才调用这个函数,触发后续的组件加载。

  3. 动态导入 import(...) 的特性

    • 这是 ES6 引入的动态导入语法,与静态 import Home from '...' 不同:
      • 静态导入在代码解析阶段就会加载文件,无论是否使用;
      • 动态 import(...) 是 “运行时加载”,只有当函数被调用时,才会发送请求加载目标文件(../views/Home.vue)。
  4. “按需启动” 的核心逻辑

    当路由配置为 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 楼自己的保安,只查进这栋楼的人,不是管理员不让进,其他楼不管。
  • 专业版:定义在单个路由配置中的守卫,仅在访问该路由时触发,用于特定路由的权限控制。

练习题(检验学习成果)

  1. 题目:配置一个动态路由/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>

这样知识点、代码、搞笑解读全结合,是不是既学得懂又记得牢?路由这东西看着复杂,其实就是 "保安 + 快递柜 + 套娃 + 外卖" 的组合,多写几个例子就熟啦!

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP