Vue部署后出现路由报错及ProductBox无限循环问题
背景
这是一个用于学习的Vue Demo,本地执行npm run serve无异常,但将构建后的dist/文件部署到服务器后出现以下两个问题:
问题1:路由报错 Cannot use 'in' operator to search for 'path' in undefined
报错信息
Cannot use 'in' operator to search for 'path' in undefined at Object.j [as resolve] (vue-router.mjs:3015:13) at w.fn (vue-router.mjs:2157:41) at w.run (reactivity.esm-bundler.js:190:25) at get value [as value] (reactivity.esm-bundler.js:1171:39) at w.fn (vue-router.mjs:2159:35) at w.run (reactivity.esm-bundler.js:190:25) at get value [as value] (reactivity.esm-bundler.js:1171:39) at w.fn (vue-router.mjs:2184:60) at w.run (reactivity.esm-bundler.js:190:25) at get value [as value] (reactivity.esm-bundler.js:1171:39)
已尝试操作
- 修改
router/index.js中的history: createWebHistory(process.env.BASE_URL)为history: createWebHistory() - 检查所有
router-link均包含to属性
路由配置代码
import { createRouter, createWebHistory } from 'vue-router' import store from '../store' import HomeView from '../views/HomeView.vue' import ProductView from '../views/ProductView.vue' import CategoryView from '../views/CategoryView.vue' import SearchView from '../views/SearchView.vue' import CartView from '../views/CartView.vue' import SignUpView from '../views/SignUpView.vue' import LogInView from '../views/LogInView.vue' import MyAccountView from '../views/MyAccountView.vue' import CheckoutView from '../views/CheckoutView.vue' import SuccessView from '../views/SuccessView.vue' const routes = [ { path: '/', name: 'Home', component: HomeView }, { path: '/about', name: 'AboutView', component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue') }, { path: '/sign-up', name: 'SignUp', component: SignUpView }, { path: '/log-in', name: 'LogIn', component: LogInView }, { path: '/my-account', name: 'MyAccount', component: MyAccountView, meta: { requireLogin: true } }, { path: '/search', name: 'Search', component: SearchView }, { path: '/cart', name: 'Cart', component: CartView }, { path: '/cart/success', name: 'SuccessView', component: SuccessView }, { path: '/cart/checkout', name: 'CheckoutView', component: CheckoutView, meta: { requireLogin: true } }, { path: '/:category_slug/:product_slug', name: 'Product', component: ProductView }, { path: '/:category_slug', name: 'Category', component: CategoryView } ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requireLogin) && !store.state.isAuthenticated) { next({ name: 'LogIn', query: { to: to.path } }); } else { next() } }) export default router
解决方案
确认生产环境BASE_URL配置
在vue.config.js中明确配置生产环境的publicPath,确保路由基础路径正确:module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/' : '/' // 若部署在子路径下,需修改为对应路径,例如 '/vuestore/' }process.env.BASE_URL会自动匹配该配置,无需手动修改路由中的createWebHistory参数。检查路由守卫中的状态初始化
确保Vuex的isAuthenticated状态在生产环境有正确初始值(比如默认设为false),避免因状态为undefined导致路由守卫逻辑异常。排查组件中路由方法的调用
检查所有组件中调用$router.resolve或其他路由API时,是否传入了undefined参数(比如动态生成路由路径时变量未赋值)。
问题2:HomeView中ProductBox组件无限循环
问题描述
在HomeView的mounted钩子中调用getLatestProducts()方法,通过axios请求更新latestProducts数据后触发组件重渲染,尝试在beforeCreated()、created()钩子中调用axios仍无效。
HomeView代码
<template> <div class="home"> <section class="hero is-medium is-dark mb-6"> <div class="hero-body has-text-centered"> <p class="title mb-6"> Welcome to NoobStore </p> <p class="subtitle"> The most honest store online don't sell you anything </p> </div> </section> <div class="columns is-multiline"> <div class="column is-12"> <h2 class="is-size-2 has-text-centered">Latest products</h2> </div> <ProductBox v-for="product in latestProducts" :key="product.id" :product="product" /> </div> </div> </template> <script> import axios from 'axios' import ProductBox from '@/components/ProductBox' export default { name: 'HomeView', data() { return { latestProducts: [] } }, components: { ProductBox }, mounted() { this.getLatestProducts() document.title = 'Nhan`s Store | Vuejs demo' }, methods: { async getLatestProducts() { this.$store.commit('setIsLoading', true) try { const res = await axios.get('/api/v1/latest-products/') this.latestProducts = res.data } catch(error) { console.log(error) } this.$store.commit('setIsLoading', false) } } } </script>
解决方案
检查ProductBox组件的数据源修改
确认ProductBox组件是否直接修改了传入的product对象(比如修改product的属性)。由于Vue响应式特性,子组件修改父组件的引用类型数据会触发父组件重渲染,进而导致无限循环。解决方法是在子组件中复制一份product数据再操作:// ProductBox组件中 export default { props: ['product'], data() { return { localProduct: {...this.product} // 复制本地数据 } } }添加请求防重复标志
在HomeView中添加isFetching标志,防止请求重复触发:data() { return { latestProducts: [], isFetching: false // 添加标志位 } }, methods: { async getLatestProducts() { if (this.isFetching) return; // 正在请求则直接返回 this.isFetching = true; this.$store.commit('setIsLoading', true) try { const res = await axios.get('/api/v1/latest-products/') this.latestProducts = res.data } catch(error) { console.log(error) } this.$store.commit('setIsLoading', false) this.isFetching = false; // 请求完成后重置标志 } }排查ProductBox的生命周期钩子
检查ProductBox组件的mounted、updated等钩子中是否有触发父组件数据更新的逻辑(比如调用父组件方法、触发事件导致父组件重新请求数据),若有则调整逻辑避免循环触发。
内容的提问来源于stack exchange,提问作者Trọng Nhân

