引语

在传统的前端开发模式中,一个特定的URL地址通常对应着服务器上的一个独立HTML页面。当用户在浏览器中切换地址时,浏览器会向服务器发送全新的请求,并重新加载、刷新整个页面。

但在现代Web开发中,基于Vue构建的单页面应用打破了这一常态。在单页面架构中,整个项目在运行期间通常只包含一份HTML文件。当页面内容发生切换时,应用并不会重新向服务器请求新页面,而是通过前端动态地卸载旧组件、挂载新组件来实现。

为了优雅、高效地管理这些组件之间的切换逻辑,Vue官方推出了核心路由管理器——Vue Router。从本质上来说,前端路由系统就是建立URL地址与组件之间的映射关系

一、 快速落地:Vue Router 4 基础配置与骨架搭建

在Vue 3项目中,我们通常使用Vue Router来搭建路由骨架。创建并实例化一个路由器对象,需要借助官方提供的 createRouter 方法。

它的基本结构配置如下:

import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'

// 创建路由器实例
const router = createRouter({
    history: createWebHistory(), // 指定路由工作模式
    routes: [                     // 配置路由规则映射表
        {
            path: '/',
            component: Home
        },
        {
            path: '/about',
            component: About
        }
    ]
})

export default router

createRouter() 的配置对象中,最核心的两个选项分别是:

  1. history:用于明确指定路由器底层采用什么样的工作模式。
  2. routes:用于维护一个路由规则数组,每一个对象都代表了一条“路径到组件”的映射线。例如,当访问根路径 / 时渲染 Home 组件,访问 /about 时渲染 About 组件。

二、 架构抉择:深度解密 Hash 与 History 两种工作模式

Vue Router 提供了两种最常见的工作模式,它们在 URL 表现形式、底层运作机制以及线上部署要求上面有着本质的区别。

2.1 Hash 模式

  • 创建方式:使用 createWebHashHistory()
  • 表现形式:URL 中会夹杂一个 # 符号,形如 http://localhost:5173/#/home
  • 运作原理# 及其后面的部分被称为 Hash 值。浏览器拥有一个特性:修改 URL 中的 Hash 值时,绝不会向服务器重新发送网络请求。Vue Router 利用这一特性,通过监听浏览器的 hashchange 事件,即可捕获路径变化并动态替换组件。
  • 优缺点:优点是极其简单,对后端服务器没有任何特殊要求,开箱即用。缺点是路径中带 # 不够美观,对搜索引擎抓取不够友好。

2.2 History 模式

  • 创建方式:使用 createWebHistory()
  • 表现形式:URL 与传统多页面网站完全一致,形如 http://localhost:5173/home
  • 运作原理:基于 HTML5 标准提供的 History API(如 pushStatereplaceState)来实现无需刷新的页面路径变更。
  • 致命痛点:由于去掉了 #,路径看起来像一个真实的服务器资源地址。如果用户在 http://localhost:5173/user 页面上手动执行刷新,浏览器会固执地直接向服务器请求 /user 这个独立的物理资源。若服务器没有配置任何重定向规则,便会直接抛出 404 错误。因此,History 模式上线部署时,必须依赖后端服务器进行额外配置,将所有未匹配到的资源重定向到 index.html

2.3 Hash 模式与 History 模式多维对比

对比维度 Hash 模式 History 模式
URL 表现 带有 # 符号(如 /#/about # 符号,干净整洁(如 /about
美观程度 一般,略显冗余 较好,符合现代 Web 审美
SEO 支持 较差 较好
服务器配置 完全不需要额外处理 必须配置,否则刷新页面报 404
企业选择 多用于内网系统、轻量工具 企业级商业项目优先推荐

三、 声明式导航升级:组件 <router-link> 与命名路由

在 Vue Router 中,我们很少直接编写原生的 <a> 标签来进行跳转。官方提供了专用的 <router-link> 内置组件。

3.1 字符串写法 vs 对象写法

最基础的声明式导航使用 to 属性指定字符串路径:

<router-link to="/home">首页</router-link>

然而在真实项目开发中,更推荐使用动态绑定对象的写法:

<router-link :to="{ path: '/home' }">首页</router-link>

核心优势:对象写法拥有极强的可扩展性,它是后续在导航中携带参数(Query 或 Params)的必备前提。

3.2 企业级规范:命名路由(Named Routes)

随着项目规模和复杂度的不断攀升,我们的页面路径往往会变得非常冗长且层级复杂,例如 /admin/user/setting/profile。如果我们在业务代码中硬编码堆砌这些路径字符串,一旦未来产品经理提出路径调整需求,维护成本将令人崩溃。

此时,命名路由便是最佳解法。我们可以在配置项中为路由规则赋予一个唯一的 name 属性:

{
    path: '/user/detail/info/secure',
    name: 'userInfo',
    component: UserInfo
}

在组件中进行跳转时,直接绑定 name 即可:

<router-link :to="{ name: 'userInfo' }">用户安全中心</router-link>

这样一来,无论底层的 path 如何演变,只要 name 保持不变,所有的跳转代码均无需任何修改,大大增强了系统的健壮性。

四、 结构进化:嵌套路由(Nested Routes)的多层页面设计

真实的中后台管理系统往往拥有多层嵌套的复合页面结构:

个人中心
 ├─ 我的订单
 ├─ 我的收藏
 └─ 我的地址

在 Vue Router 中,我们通过配置 children 属性来实现多级嵌套关系。

{
    path: '/user',
    component: UserLayout, // 父骨架组件
    children: [
        {
            path: 'order',   // 子路由路径:前方切勿加 "/" 符号!
            component: Order
        },
        {
            path: 'collect',
            component: Collect
        }
    ]
}

避坑关键点:配置 children 内部的子路由时,子路径(如 order)前面千万不要加 /。如果加了 /,Vue Router 会将其视为根路径,从而脱离原有的层级关系。

在编写好映射规则后,还需要在父组件(如 UserLayout.vue)的模板内部预留一个 <router-view /> 占位插槽。当浏览器地址切换至 /user/order 时,Order 子组件就会自动填充渲染到父组件的 <router-view /> 内部。

五、 数据传递核心:Query 传参与 Params 传参的生死博弈

页面间的通信和参数传递是前端业务开发中最频繁出现的需求。Vue Router 为我们提供了两套各司其职的传参技术方案。

5.1 Query 参数(查询字符串)

  • 核心本质:等同于传统 HTTP 请求中的 URL 查询字符串(形如 /user?id=1&name=Tom)。

  • 发送方式

    <router-link :to="{ path: '/user', query: { id: 1, name: 'Tom' } }">用户信息</router-link>
    
  • 接收方式

    import { useRoute } from 'vue-router'
    const route = useRoute()
    console.log(route.query.id, route.query.name)
    
  • 技术特点:不需要提前在路由映射表里修改 path 占位;参数完全暴露在浏览器的地址栏中;最大的优势是刷新页面时参数绝对不会丢失

5.2 Params 参数(路径参数)

  • 核心本质:属于 RESTful 风格的路径段落参数(形如 /user/1/Tom)。

  • 规则配置:必须在路由表里通过 : 符号提前进行参数占位

    {
        path: '/user/:id/:name',
        name: 'user',
        component: User
    }
    
  • 发送方式

    <router-link :to="{ name: 'user', params: { id: 1, name: 'Tom' } }">用户信息</router-link>
    
  • 接收方式

    const route = useRoute()
    console.log(route.params.id)
    
  • 致命规矩

    当使用动态对象写法传递 params 参数时,只能配合 name(命名路由)进行跳转,绝对不允许配合 path 写法! 例如 :to="{ path: '/user', params: {...} }" 会导致参数无法正确传递,这是新手开发中最容易踩中的盲区。此外,Params 传参必须提前做好占位,若不配置占位,数据无法正确接收。

六、 解耦技巧:路由的 Props 配置

在常规开发中,我们在组件内部为了拿到路由参数,往往不得不编写类似 const route = useRoute(); const id = route.params.id; 的代码。这样做的弊端非常明显:当前的组件与路由系统发生了深度强耦合。一旦未来这个组件需要被挪移到其他没有路由的环境下复用,就无法正常运转。

为了彻底打破这种耦合,Vue Router 提供了优雅的 props 配置功能

{
    path: '/user/:id',
    component: User,
    props: true // 开启布尔值模式:自动将 params 中的参数转换为组件的 Props 属性
}

User.vue 组件中,我们不需要再调用 useRoute(),直接声明并接收 props 即可:

<script setup>
// 直接接收,就像普通的父子组件传值一样,组件瞬间变得干净、高内聚
defineProps(['id'])
</script>

<template>
  <div>当前用户 ID 是:{{ id }}</div>
</template>

七、 动态掌控力:Replace 属性与编程式导航

7.1 replace 属性的妙用

默认情况下,无论是点击 <router-link> 还是触发页面切换,路由系统都会向浏览器的历史记录栈中追加(push)一条新记录。

例如,用户操作的轨迹是 页面A → 页面B → 页面C。如果此时用户点击浏览器的返回按钮,其回溯路线是 页面C → 页面B → 页面A

但在某些特定业务场景下(如“登录页登录成功后跳转到首页”,或者“支付成功后跳转到结果页”),我们不希望用户能够通过返回键退回到刚才的登录或支付页。此时我们只需为组件附加 replace 属性:

<router-link replace to="/home">进入首页</router-link>

它在底层对应的是 history.replaceState() 接口,作用是直接替换当前的历史记录,而不是像以往一样去新增记录,完美防范了不合理的退回操作。

7.2 编程式导航(Programmatic Navigation)

除了利用模板里的 <router-link> 点击标签引发跳转外,在实际研发中,我们海量的业务逻辑都需要在 JavaScript 代码中控制跳转。比如“表单校验通过后跳转”、“Ajax 请求完毕后跳转”。

在 Vue 3 组合式 API 中,我们调用 useRouter() 钩子来获取全局路由操作实例:

import { useRouter } from 'vue-router'

const router = useRouter() // 获取操作实例

// 各种灵活的控制手段:
router.push('/home')       // 向历史栈追加记录并跳转
router.replace('/home')    // 替换当前记录并跳转
router.back()              // 后退一步
router.forward()           // 前进一步
router.go(-2)              // 任意步数的前进或后退(例如后退两步)

结语

掌握 Vue Router 的核心基础,是前端开发由“页面组装”迈向“应用架构”的分水岭。需要深刻理清 Hash 与 History 的底层逻辑、切实避开 Params 传参的语法红线、灵活玩转编程式导航与解耦 Props,我们才能在开发中真正建立起成熟、完整的单页面路由认知体系,并为后续进阶学习打下牢不可破的底层基础。

更多推荐