如何在v-lazy组件中正确使用$vuetify.goTo?
问题根源
- 组件未完全渲染就触发定位:
$nextTick仅等待DOM结构更新,但移动端高高度组件(如Projects)内的图片、动态内容加载需要额外时间,此时元素实际高度未计算完成,$vuetify.goTo基于占位高度定位,自然出现偏差。 - v-lazy占位高度配置错误:
contact项的min-height缺少单位(需写成'300px'),且移动端50vh的占位远小于组件实际的150-200vh,导致滚动位置计算错误。 - 定位触发时机过早:刚设置
isActive就立刻执行跳转,未等待组件内部内容渲染到位。
解决方案
1. 修正v-lazy基础配置
确保占位高度匹配组件实际尺寸,单位统一,同时调低阈值让组件更早加载:
<v-lazy v-for="item in items" :key="item.component" :id="item.component" v-model="item.isActive" :options="{ threshold: 0 }" <!-- 阈值设0,组件一进入视口就加载 --> :min-height="item.component === 'contact' ? '300px' : '200vh'" <!-- 匹配移动端组件实际高度范围 --> > <v-component :is="item.component" /> <!-- 注意:原代码拼写错误*v-componenet* → *v-component* --> </v-lazy>
2. 优化跳转方法,等待组件完全渲染
不要仅依赖$nextTick,添加小延迟确保图片等内容加载完成后再执行定位:
methods: { async goToItem(destination) { // 激活目标组件及所有前置组件 const targetIndex = this.items.findIndex(i => i.component === destination); this.items.forEach((item, index) => { if (index <= targetIndex) { item.isActive = true; } }); // 先等DOM更新,再预留时间让内容加载(可根据实际情况调整延迟时长) await this.$nextTick(); await new Promise(resolve => setTimeout(resolve, 300)); // 执行跳转,添加动画时长适配滚动 this.$vuetify.goTo(`#${destination}`, { duration: 500, offset: 0 // 若有固定导航栏,此处需设置导航栏高度,如64 }); } }
3. 精准监听组件加载完成(可选,更可靠)
如果组件包含大量图片或异步内容,让子组件主动通知父组件加载完成:
- 子组件示例(如Projects.vue):
<script> export default { mounted() { const imgs = this.$el.querySelectorAll('img'); let loadedCount = 0; // 统计已加载的图片 imgs.forEach(img => { if (img.complete) loadedCount++; }); // 给未加载的图片添加监听 imgs.forEach(img => { if (!img.complete) { img.addEventListener('load', () => { loadedCount++; if (loadedCount === imgs.length) this.$emit('loaded'); }); } }); // 无图片时直接通知加载完成 if (imgs.length === 0) this.$emit('loaded'); } }; </script>
- 父组件监听并等待所有必需组件加载完成后再跳转:
<template> <v-lazy ...> <v-component :is="item.component" @loaded="markComponentLoaded(item.component)" /> </v-lazy> </template> <script> export default { data() { return { // ... loadedComponents: new Set() // 存储已加载完成的组件名 }; }, methods: { markComponentLoaded(component) { this.loadedComponents.add(component); }, async goToItem(destination) { const targetIndex = this.items.findIndex(i => i.component === destination); const requiredComponents = this.items.slice(0, targetIndex + 1).map(i => i.component); // 激活所有必需组件 requiredComponents.forEach(comp => { const item = this.items.find(i => i.component === comp); if (item) item.isActive = true; }); // 等待所有必需组件加载完成 await new Promise(resolve => { const checkLoaded = () => { const allDone = requiredComponents.every(comp => this.loadedComponents.has(comp)); if (allDone) resolve(); else setTimeout(checkLoaded, 100); }; checkLoaded(); }); // 执行定位 this.$vuetify.goTo(`#${destination}`); } } }; </script>
内容的提问来源于stack exchange,提问作者Ethr
相关产品推荐
相关产品推荐

