Vue Router同组件表单提交跳转不刷新 求更优实现方案
Vue Router复用组件时路由更新不触发数据刷新问题
问题描述
项目中多个路由(根路径/、收藏页/favorites、搜索结果页/results/:search)复用同一个Home组件,通过路径参数区分需要拉取的不同数据类型。其余路由跳转均运行正常,但提交搜索表单、携带搜索关键词跳转至结果页路由时,Vue Router会复用已挂载的组件实例而非重新渲染,导致组件内读取的仍是旧路由的数据,未触发对应的数据更新逻辑。
先后尝试调用router.go()方法、使用beforeRouteUpdate导航守卫,均未达到预期效果。
问题原因
beforeRouteUpdate导航守卫仅对路由配置中直接渲染的组件生效(即路由规则里component字段对应的组件),把守卫写在Home组件引入的子组件Cards.vue中,自然不会触发。- 最初的搜索表单未阻止原生submit默认行为,会触发页面刷新打断路由跳转逻辑。
原始代码
Router.js
import Vue from "vue"; import Router from "vue-router"; import Home from "./views/Home.vue"; import Detail from "./views/Detail.vue"; Vue.use(Router); export default new Router({ routes: [ { path: "/", name: "home", component: Home }, { path: "/favorites", name: "favorites", component: Home }, { path: "/results/:search", name: "results", component: Home }, { path: "/detail/:id", name: "detail", component: Detail } ] });
Cards.vue(Home组件引入的子组件)
<template> <div class="cardContainer"> <Filters /> <div v-if="$store.state.movieList.length > 0" class="cardContainer-container" > <Card v-for="(item, index) in $store.state.movieList" :key="index" :id="item._id" :title="item.title" :img="item.image" :year="item.year" :description="item.description" /> </div> <div v-else class="cardContainer-container"> Not fun </div> </div> </template> <script> import Card from "./Card.vue"; import Filters from "./Filters.vue"; export default { name: "cardContainer", components: { Card, Filters, }, data() { return { path: this.$route.name, params: this.$route.params.search, }; }, beforeMount(){ console.log("path", this.path);// 导航栏链接跳转时正常触发,搜索按钮跳转时不触发 }, beforeRouteUpdate(to, from, next){ console.log("I hope this works", to); // 该守卫始终不触发 next(); } }; </script>
SearchBar.vue
<template> <form class="searchBar" v-on:submit="onSubmit"> <input type="text" v-model="search" /> <button type="submit"> Search </button> </form> </template> <script> export default { name: "searchBar", data() { return { search: "" }; }, methods: { onSubmit() { // this.$router.go(`/results/${this.search}`) // 无效 this.$router.push(`/results/${this.search}`); this.search = ""; } } }; </script>
临时方案
目前通过在Cards组件中添加$watch监听$route.params.search变化,同时给搜索表单加@submit.prevent阻止默认提交行为临时解决问题,修改后的Cards.vue核心代码如下:
<script> import Card from "./Card.vue"; import Filters from "./Filters.vue"; export default { name: "cardContainer", components: { Card, Filters, }, data() { return { path: this.$route.name, params: this.$route.params.search, }; }, beforeMount() { console.log("path", this.path); // 触发对应数据拉取 }, created() { this.$watch( () => this.$route.params.search, () => { this.params = this.$route.params.search; // 触发对应数据拉取 } ); }, }; </script>
更规范的实现方案
方案1:在路由级组件统一处理数据拉取逻辑(官方推荐)
把数据拉取逻辑统一抽到路由直接渲染的Home.vue中,利用beforeRouteUpdate守卫处理路由复用场景,逻辑集中易维护:
<!-- Home.vue 核心代码 --> <script> export default { beforeRouteUpdate(to, from, next) { // 路由参数变化时拉取对应数据 this.fetchDataByRoute(to) next() }, mounted() { // 首次进入组件时拉取数据 this.fetchDataByRoute(this.$route) }, methods: { fetchDataByRoute(route) { switch(route.name) { case 'home': // 拉取首页热门电影列表 break case 'favorites': // 拉取用户收藏列表 break case 'results': const keyword = route.params.search // 拉取关键词对应的搜索结果 break } } } } </script>
子组件Cards.vue只负责读取store中的数据渲染,不需要关心路由变化逻辑。
方案2:给router-view绑定key强制组件重渲染(轻量场景适用)
如果不想单独写路由守卫,可以直接在根组件的<router-view>上绑定$route.fullPath作为唯一key,路由变化时会自动触发组件完整重渲染,直接走mounted等生命周期钩子,不需要额外处理复用逻辑:
<!-- App.vue --> <template> <div id="app"> <router-view :key="$route.fullPath" /> </div> </template>
注意无论用哪种方案,搜索表单的submit事件都要加.prevent修饰符,避免原生提交触发页面刷新:
<form class="searchBar" @submit.prevent="onSubmit">
内容的提问来源于stack exchange,提问作者Acuervov
相关产品推荐
相关产品推荐

