源本科技 | 码上会

Vue3 编程式导航

2026/04/23
9
0

引言

编程式导航是 Vue Router 4 专为 Vue 3 提供的代码级路由控制方案,通过调用路由实例 API 实现应用内的路由切换,完全脱离 HTML 链接的依赖。开发者可在表单提交、权限校验、异步请求等业务逻辑中,动态控制跳转时机、传递路由参数、操作浏览器历史记录,实现路径与组件数据的联动更新,是构建复杂交互单页应用(SPA)的核心能力。

概述

编程式导航指通过 JavaScript 代码 主动控制路由跳转,替代 <router-link> 声明式导航。在 Vue 3 组合式 API 中,必须通过 useRouter() 函数获取路由实例,这是执行所有编程式导航的唯一入口。该方案支持动态传参、条件跳转、历史记录管理,与业务逻辑深度绑定,适配登录验证、表单提交、权限拦截等复杂场景。

核心路由切换方法

Vue Router 4 提供 3 类标准化导航方法,对应不同的浏览器历史记录操作规则。

导航新增历史记录

router.push() 是最常用的导航方法,向历史栈添加新记录,点击浏览器后退可返回上一页面。

  • 声明式等价写法:<router-link :to="...">

  • 编程式核心 API:router.push(...)

支持字符串路径、路由对象两种传参方式,Vue Router 4 强制规则pathparams 不可混用,参数会失效。

import { useRouter } from 'vue-router';
const router = useRouter();

// 1. 字符串路径(最简写法)
router.push('/home/dashboard');

// 2. 路径对象
router.push({ path: '/home/products' });

// 3. 命名路由 + params(推荐,适配动态路由)
router.push({ name: 'Product', params: { id: '123' } });

// 4. 带查询参数(? 拼接)
router.push({ path: '/product', query: { id: '123' } });

// 5. 带锚点(hash)
router.push({ path: '/about', hash: '#team' });

替换当前历史记录

router.replace() 作用与 push 一致,不会新增历史记录,直接替换当前栈条目,后退无法返回当前页。
适用于登录页、支付页等禁止用户返回的场景。

  • 声明式等价写法:<router-link :to="..." replace>

  • 编程式核心 API:router.replace(...)

// 标准写法
router.replace({ path: '/home/dashboard' });

// 简化写法(等价于 replace)
router.push({ path: '/home', replace: true });

历史记录步进导航

router.go() 接收整数参数,控制历史记录前进 / 后退,等价于 window.history.go(n)
同时提供快捷方法 back()forward()

// 后退 1 页(等价于浏览器返回按钮)
router.go(-1);
router.back();

// 前进 1 页
router.go(1);
router.forward();

// 前进 3 页
router.go(3);

// 无对应记录时,静默失效
router.go(-100);

路由参数传递

结合前文动态路由、嵌套路由,提供企业级参数传递规范:

  1. 动态路径参数:必须使用 命名路由 + params

  2. 查询参数:可搭配 path/name 自由使用

  3. 嵌套路由跳转:推荐使用命名路由,避免硬编码长路径

登录功能案例

基于 Element Plus 实现登录表单校验,结合编程式导航完成登录验证,衔接前文嵌套路由配置,登录成功后跳转到后台仪表盘,登录失败给出友好提示。创建 src/views/Login.vue

<template>
  <div class="login-container">
    <el-card class="login-card">
      <h1 class="text-center text-2xl font-bold mb-8">欢迎登录</h1>
      <el-form :model="form" :rules="rules" ref="formRef">
        <el-form-item label="账号:" prop="username">
          <el-input v-model="form.username" placeholder="请输入账号" clearable />
        </el-form-item>
        <el-form-item label="密码:" prop="password">
          <el-input v-model="form.password" type="password" placeholder="请输入密码" show-password />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="submitForm" block>登录</el-button>
        </el-form-item>
      </el-form>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import type { FormInstance, FormRules } from 'element-plus';
// 导入路由核心 API
import { useRouter } from 'vue-router';

// 获取路由实例
const router = useRouter();
// 表单实例
const formRef = ref<FormInstance>();

// 登录表单数据
const form = reactive({
  username: '',
  password: ''
});

// 表单校验规则
const rules = reactive<FormRules>({
  username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
  password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
});

// 登录提交逻辑
const submitForm = async () => {
  const formEl = formRef.value;
  if (!formEl) return;

  // 执行表单校验
  await formEl.validate((valid) => {
    if (valid) {
      // 模拟账号密码校验
      if (form.username === 'admin' && form.password === '123456') {
        ElMessage.success('登录成功');
        // 编程式导航:跳转到仪表盘(替换历史记录,禁止返回登录页)
        router.replace({ name: 'Dashboard' });
      } else {
        ElMessage.error('账号或密码错误');
      }
    }
  });
};
</script>

<style scoped>
.login-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #f5f7fa;
}

.login-card {
  width: 360px;
  padding: 40px;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
</style>
  • 追加路由 src\router\index.ts

// 登录路由
{
  path: '/login',
  name: 'Login',
  component: () => import('../views/Login.vue')
},

其他补充

导航异步处理

Vue Router 4 导航方法返回 Promise,支持异步监听跳转结果:

// 异步等待导航完成
router.push({ name: 'Dashboard' }).then(() => {
  console.log('跳转成功');
}).catch((err) => {
  console.log('跳转失败', err);
});

命名路由优先原则

企业开发中禁止硬编码路径,统一使用命名路由,便于后期维护:

// 推荐(无硬编码)
router.push({ name: 'Dashboard' });

// 不推荐
router.push('/home/dashboard');