Code前端首页关于Code前端联系我们

一、表单提交后,怎么用Vue Router跳转到新页面并传参?

terry 8小时前 阅读数 12 #Vue

在Vue项目里,表单(Form)是收集用户输入的核心载体,而Vue Router负责管理页面路由跳转,把这俩结合好能解决很多实际开发需求,比如表单提交后跳转到结果页、根据路由参数回显表单数据这些场景,但刚接触的同学可能会疑惑“Vue Router 怎么和表单配合?不同场景该咋实操?” 下面就从常见场景、具体实现、避坑要点这些角度,把两者结合的逻辑和技巧拆明白。

表单提交后跳转是很常见的需求,比如登录后跳个人中心、发布内容后跳详情页,这里分**同步提交(先验证再跳转)**和**异步提交(调接口后跳转)**两种情况,还要注意路由传参的细节。

同步提交:先验证,再跳转

场景:用户填登录表单,验证通过后跳转到个人中心,还要把用户名带过去显示欢迎语。

代码示例(Composition API):

<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="username" placeholder="用户名" />
    <button type="submit">提交</button>
  </form>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { ref } from 'vue'
const router = useRouter()
const username = ref('')
const handleSubmit = () => {
  // 简单验证(实际项目可能用VeeValidate等库)
  if (username.value.trim()) {
    // 用query传参,刷新页面参数不会丢
    router.push({
      path: '/profile',
      query: { username: username.value }
    })
  } else {
    alert('用户名不能为空~')
  }
}
</script>

目标页面(Profile.vue)接收参数:

<template>
  <div>欢迎回来,{{ $route.query.username }}</div>
</template>

异步提交:调接口后,带后端返回数据跳转

场景:发布文章后,用后端返回的文章ID跳转到详情页。

代码示例:

<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="title" placeholder="文章标题" />
    <textarea v-model="content"></textarea>
    <button type="submit">发布</button>
  </form>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { ref } from 'vue'
import { createArticle } from '@/api/article' // 假设的接口函数
const router = useRouter()
const title = ref('')
const content = ref('')
const handleSubmit = async () => {
  try {
    // 调接口提交数据
    const res = await createArticle({ title: title.value, content: content.value })
    // 用params传参(需在路由配置定义动态段 :id)
    router.push({
      name: 'ArticleDetail', // 路由配置的name
      params: { id: res.data.id }
    })
  } catch (error) {
    console.error('发布失败', error)
    alert('发布出错啦,再试试?')
  }
}
</script>

路由配置(router/index.js):

const routes = [
  {
    path: '/article/:id', // 动态段:id
    name: 'ArticleDetail',
    component: () => import('@/views/ArticleDetail.vue')
  }
]

目标页面(ArticleDetail.vue)接收参数:

<template>
  <div>这篇文章的ID是:{{ $route.params.id }}</div>
</template>

query和params传参咋选?

  • query:参数拼在URL后面(如?username=xxx),刷新页面参数还在,适合需要用户看到、能分享的场景;
  • params:参数藏在路由路径里(如/article/123),但如果路由配置没写动态段(:id),刷新会丢失参数,所以如果是“页面内部逻辑传参,不想暴露在URL”,可用params;但要持久化参数,优先选query。

进入页面时,怎么根据路由参数回显表单数据?

场景:用户点列表页的“编辑”按钮,进入编辑页,路由带了itemId,需要根据itemId查详情,把数据填到表单里,还要处理“同一路由,参数变化(比如点不同编辑按钮)”时的数据更新。

页面初始加载时回显

onMounted(Composition API)或created(Options API)钩子,在组件加载时从$route取参数,调接口拿数据,再赋值给表单。

代码示例:

<template>
  <form>
    <input v-model="form.title" placeholder="标题" />
    <textarea v-model="form.content"></textarea>
  </form>
</template>
<script setup>
import { onMounted, reactive } from 'vue'
import { useRoute } from 'vue-router'
import { getArticleDetail } from '@/api/article'
const route = useRoute()
const form = reactive({ '',
  content: ''
})
onMounted(async () => {
  const articleId = route.params.id // 取路由里的:id
  const res = await getArticleDetail(articleId)
  form.title = res.data.title
  form.content = res.data.content
})
</script>

路由参数变化时,重新回显数据

比如列表页多个编辑按钮,都跳转到同一个编辑组件,但itemId不同,这时候组件不会重新加载,只用onMounted数据不会更新,得监听路由变化

watch监听route

<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'
// ...其他代码(form定义、接口引入)
const route = useRoute()
watch(route, async (newRoute) => {
  const articleId = newRoute.params.id
  const res = await getArticleDetail(articleId)
  form.title = res.data.title
  form.content = res.data.content
}, { immediate: true }) // immediate: true → 组件加载时立即执行一次
</script>

用导航守卫beforeRouteUpdate(Options API更常用)
如果用Options API:

<script>
export default {
  data() {
    return {
      form: { title: '', content: '' }
    }
  },
  async beforeRouteUpdate(to) { // to是目标路由
    const articleId = to.params.id
    const res = await getArticleDetail(articleId)
    this.form.title = res.data.title
    this.form.content = res.data.content
  }
}
</script>

表单多步骤(分步表单)场景,怎么用Vue Router管理步骤?

场景:注册流程分“手机号验证→密码设置→个人信息”三步,每一步对应一个路由(用户刷新/分享链接,步骤不丢)。

路由配置:把步骤拆成子路由

const routes = [
  {
    path: '/register',
    component: RegisterLayout, // 布局组件(放步骤导航)
    children: [
      { path: 'step1', name: 'RegisterStep1', component: Step1 },
      { path: 'step2', name: 'RegisterStep2', component: Step2 },
      { path: 'step3', name: 'RegisterStep3', component: Step3 },
      { path: '', redirect: 'step1' } // 默认进第一步
    ]
  }
]

步骤导航组件:控制下一步、上一步

在布局组件(RegisterLayout.vue)里做导航:

<template>
  <div class="steps-nav">
    <div>当前步骤:{{ currentStep }}</div>
    <button @click="goPrev" v-if="currentStep > 1">上一步</button>
    <button @click="goNext">下一步</button>
  </div>
  <router-view></router-view> <!-- 显示当前步骤的组件 -->
</template>
<script setup>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
// 从当前路由解析步骤(如/register/step1 → step1)
const currentStep = computed(() => router.currentRoute.value.path.split('/').pop())
const goPrev = () => {
  if (currentStep.value === 'step2') {
    router.push('/register/step1')
  } else if (currentStep.value === 'step3') {
    router.push('/register/step2')
  }
}
const goNext = () => {
  if (currentStep.value === 'step1') {
    // 先验证Step1表单是否合法(假设用isStep1Valid标记)
    const isStep1Valid = true // 实际要写验证逻辑
    if (isStep1Valid) {
      router.push('/register/step2')
    } else {
      alert('Step1的信息没填对哦~')
    }
  } else if (currentStep.value === 'step2') {
    router.push('/register/step3')
  } else if (currentStep.value === 'step3') {
    alert('注册完成!') // 这里调最终提交接口
  }
}
</script>

多步骤表单数据咋共享?

因为每一步是不同组件,得用全局状态管理(如Pinia/Vuex)存表单数据,以Pinia为例:

新建store(stores/register.js):

import { defineStore } from 'pinia'
export const useRegisterStore = defineStore('register', {
  state: () => ({
    form: {
      phone: '',
      password: '',
      nickname: ''
    }
  }),
  actions: {
    updateField(field, value) {
      this.form[field] = value
    }
  }
})

在Step1组件里更新数据:

<template>
  <form>
    <input 
      v-model="phone" 
      placeholder="手机号" 
      @input="updateStore('phone', phone)" 
    />
  </form>
</template>
<script setup>
import { ref, watch } from 'vue'
import { useRegisterStore } from '@/stores/register'
const store = useRegisterStore()
const phone = ref(store.form.phone)
// 输入时同步到store
watch(phone, (newVal) => {
  store.updateField('phone', newVal)
})
</script>

拦截非法跳转(比如直接URL输step3)

路由守卫,比如全局前置守卫beforeEach

// router/index.js
import { useRegisterStore } from '@/stores/register'
router.beforeEach((to) => {
  if (to.path.includes('/register/step')) {
    const step = to.path.split('/').pop()
    const store = useRegisterStore()
    // Step2需要Step1的phone,Step3需要Step2的password
    if (step === 'step2' && !store.form.phone) {
      alert('请先完成第一步~')
      return false // 阻止跳转
    }
    if (step === 'step3' && !store.form.password) {
      alert('请先完成第二步~')
      return false
    }
  }
})

表单筛选条件,怎么和URL参数实时同步?

场景:商品列表页的筛选表单(分类、价格、排序),选择后URL的query参数更新(刷新页面/分享链接,筛选条件保留)。

表单变化时,更新URL的query

watch监听表单数据,变化时调用router.push更新query。

代码示例:

<template>
  <form>
    <select v-model="form.category">
      <option value="all">全部</option>
      <option value="electronics">电子产品</option>
    </select>
    <input type="number" v-model="form.priceMin" placeholder="最低价格" />
    <input type="number" v-model="form.priceMax" placeholder="最高价格" />
    <button @click="handleSort">按价格排序</button>
  </form>
  <div v-for="item in goodsList" :key="item.id">{{ item.name }}</div>
</template>
<script setup>
import { reactive, watch, ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { getGoodsList } from '@/api/goods'
const router = useRouter()
const route = useRoute()
// 表单数据
const form = reactive({
  category: 'all',
  priceMin: '',
  priceMax: '',
  sort: 'default'
})
// 页面加载时,用URL的query初始化表单
watch(route, (newRoute) => {
  form.category = newRoute.query.category || 'all'
  form.priceMin = newRoute.query.priceMin || ''
  form.priceMax = newRoute.query.priceMax || ''
  form.sort = newRoute.query.sort || 'default'
}, { immediate: true })
// 表单变化时,更新URL的query
watch(form, (newForm) => {
  router.push({
    query: {
      category: newForm.category,
      priceMin: newForm.priceMin,
      priceMax: newForm.priceMax,
      sort: newForm.sort
    }
  })
}, { deep: true }) // form是对象,要深度监听
// 根据表单和URL参数,获取列表数据
const goodsList = ref([])
watch([form, route], async () => {
  const res = await getGoodsList(form)
  goodsList.value = res.data
}, { immediate: true })
// 处理排序按钮
const handleSort = () => {
  form.sort = 'priceDesc' // 假设点击后按价格降序
}
</script>

防抖优化:避免频繁路由跳转

如果表单输入很频繁(比如价格输入时实时更新URL),会导致频繁路由跳转和接口请求,用防抖函数延迟更新:

引入lodash的debounce(或自己实现):

<script setup>
import { debounce } from 'lodash'
// ...其他代码
const debouncedUpdateRoute = debounce((newForm) => {
  router.push({ query: newForm })
}, 300) // 300毫秒延迟
watch(form, (newForm) => {
  debouncedUpdateRoute({
    category: newForm.category,
    priceMin: newForm.priceMin,
    priceMax: newForm.priceMax,
    sort: newForm.sort
  })
}, { deep: true })
</script>

用户误点路由,怎么拦截未保存的表单?

场景:用户填了一半表单,点了侧边栏的其他菜单,要提示“是否放弃修改”。

组件内守卫:beforeRouteLeave

在表单组件里,用onBeforeRouteLeave(Composition API)或beforeRouteLeave(Options API)拦截:

<template>
  <form>
    <input v-model="username" />
    <input v-model="password" />
  </form>
</template>
<script setup>
import { ref, watch } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
const username = ref('')
const password = ref('')
let isFormDirty = ref(false)
// 监听表单变化,标记“是否有未保存内容”
watch([username, password], () => {
  isFormDirty.value = true
})
// 离开路由前拦截
onBeforeRouteLeave((to, from, next) => {
  if (isFormDirty.value) {
    const isConfirm = window.confirm('表单有未保存内容,确定要离开吗?')
    if (isConfirm) {
      next() // 确认离开
    } else {
      next(false) // 取消离开,留在当前页
    }
  } else {
    next() // 没修改,直接离开
  }
})
</script>

SPA模式下,表单重复提交咋避免?

场景:用户提交表单后跳转到结果页,按浏览器“后退→前进”,可能重新提交表单。

跳转后清空表单/标记状态

提交成功后,跳转到新路由,并清空原表单数据:

const handleSubmit = async () => {
  await submitForm() // 调提交接口
  router.push('/submit-success')
  // 清空表单
  form.username = ''
  form.password = ''
}

结果页用meta标记,阻止回退重复提交

路由配置加meta

{
  path: '/submit-success',
  component: SubmitSuccess,
  meta: { isSubmitPage: true }
}

全局守卫拦截回退:

router.beforeEach((to, from) => {
  if (from.meta.isSubmitPage && to.path === '/form-page') {
    alert('已

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门