Vue3页面通过router-link跳转后内容不加载,需刷新才显示
解决Vue3路由跳转后页面冻结、v-for内容不渲染的问题
核心问题分析
从你的代码和描述来看,页面冻结、v-for内容无法正常渲染的主要原因集中在以下几点:
- Firebase实时监听重复触发,导致
keyAreas数组无限累加,页面渲染压力过大 - 使用数组索引作为v-for的key,引发Vue DOM复用错误,导致渲染异常
- 未处理
user异步加载的情况,初始时user.value为null,导致后续查询逻辑异常 - 模板中用到的
updateChartData方法未定义,引发报错中断渲染流程
分步解决方案
1. 修复Firebase监听逻辑,避免数据累加与内存泄漏
你使用on("value")持续监听数据变化,但每次回调都会往keyAreas里push数据且未清空旧数据,导致数组无限膨胀。同时组件卸载时未取消监听,会造成内存泄漏。
- 每次监听回调执行前,先清空
keyAreas - 组件卸载时调用监听返回的取消函数,终止监听
- 通过
watch监听user的变化,确保用户信息加载完成后再执行查询
2. 替换v-for的key为唯一标识
使用数组索引:key="index"会导致Vue在数组元素变化时复用错误的DOM节点,引发渲染异常。改用每个区域的title作为key,因为title是唯一且稳定的标识。
3. 处理异步用户数据
getUser返回的user是异步加载的,初始时user.value为null,直接访问user.value.uid会报错。通过watch监听user的变化,确保用户存在后再执行Firebase查询。
4. 补充缺失的updateChartData方法
模板中调用了updateChartData但代码未定义,这会导致控制台报错,中断页面渲染流程,需要实现该方法。
优化后的完整代码
<template> <div class="enquiry"> <div class="row"> <!-- 使用唯一的title作为v-for的key --> <div class="col-6" v-for="p in keyAreas" :key="p.title"> <div class="card" style="margin-bottom: 10px"> <div class="card-body"> <h2 class="card-title"> {{ p.number }} <span class="card-title" style="float: right">{{ p.title }}</span> </h2> <h5 class="card-text"> Properties for rent <span class="card-title" style="float: right; margin-top: -5px"> <Doughnut :chart-data="updateChartData(p.number, totalPropertiesNumber)" :width="80" :height="80" /> </span> </h5> </div> </div> </div> </div> </div> </template> <script> import { ref, watch, onUnmounted } from "vue"; import { projectDatabase } from "../../firebase/config"; import getUser from "../../composables/getUser"; import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale, } from "chart.js"; ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale); export default { setup() { const { user } = getUser(); const company = ref({}); const keyAreas = ref([]); const totalPropertiesNumber = ref(0); let databaseListener = null; // 定义区域列表,无需用ref,因为不会修改原数组结构 const allLGAs = [ { title: "Abuja", number: 0 }, { title: "Banana Island", number: 0 }, { title: "Bluewaters Lagos", number: 0 }, { title: "Benin City", number: 0 }, { title: "Eko Atlantic", number: 0 }, ]; const filterLocation = (item, query, filter) => { return item[filter] === query; }; // 实现模板中用到的图表数据更新方法 const updateChartData = (current, total) => { return { labels: ["Current", "Remaining"], datasets: [{ data: [current, total - current], backgroundColor: ["#36A2EB", "#FF6384"], hoverBackgroundColor: ["#36A2EB", "#FF6384"] }] }; }; // 监听user状态,确保用户加载完成后执行查询 watch(user, (newUser) => { if (!newUser?.uid) return; // 移除之前的监听,避免重复触发 if (databaseListener) { databaseListener(); } // 绑定新的实时监听 databaseListener = projectDatabase .ref("users") .child(newUser.uid) .child("company") .on("value", (snapshot) => { const companyData = snapshot.val() || {}; company.value = companyData; // 计算总房产数量 totalPropertiesNumber.value = Object.keys(companyData).length; // 清空旧数据,重新计算有效区域 keyAreas.value = []; allLGAs.forEach((area) => { const count = Object.values(companyData) .filter(item => filterLocation(item, area.title, "location")) .length; if (count > 0) { // 复制对象,避免修改原数组元素 keyAreas.value.push({ ...area, number: count }); } }); }); }, { immediate: true }); // 组件卸载时取消监听,防止内存泄漏 onUnmounted(() => { if (databaseListener) { databaseListener(); } }); return { keyAreas, totalPropertiesNumber, updateChartData }; }, }; </script>
额外注意事项
- 如果不需要实时更新数据,可以将
on("value")改为once("value"),避免持续监听带来的性能开销 - 对大型数组进行过滤计算时,可以考虑使用
computed属性,让Vue自动缓存计算结果,提升性能 - 检查其他出现问题的页面是否存在类似的监听逻辑或key使用错误,统一修复
内容的提问来源于stack exchange,提问作者Deji James
相关产品推荐
相关产品推荐

