Vue中点击显示组件报错index未定义,如何按数组值控制组件显示?
问题描述
页面展示一组女性列表,每个女性对应一个Contestant组件。需求是点击任意女性条目时显示对应的组件,支持同时加载多个组件。原本不加条件能正常显示三个组件,但给Contestant加上v-if="activeWindows.includes(index)"后,页面无内容且报错:Property "index" was accessed during render but is not defined on instance.。用户疑惑index已经在v-for中定义了,完整代码如下:
<template> <div class="page" id="womensPage"> <div> <Contestant v-for="(woman, index) in women" :key="index" :contestant="woman" v-if="activeWindows.includes(index)" /> </div> <div class="pageContent"> <h1 class="center fs26 fw800 red uc mb50">Females</h1> <div class="contestantListWrap"> <div class="womenList flex between"> <div class="woman mb10" v-for="(woman, index) in women" :key="index" @click="loadWoman(index)"> <img :src="woman.picture" class="womanImg" /> <h3 class="flex red jCenter">{{ woman.firstName }} {{ woman.lastName }}</h3> <p>Age {{ woman.age }}</p> <p>Country: {{ woman.country }}</p> </div> </div> </div> </div> </div> </template> <script setup> import { ref } from "vue"; import Contestant from "@/components/Contestant.vue"; const activeWindows = ref([]); const women = ref([ { id: 1, picture: "/src/assets/img/one.jpg", firstName: "Alice", lastName: "Smith", age: 30, country: "USA", }, { id: 2, picture: "/src/assets/img/two.jpg", firstName: "Maria", lastName: "Garcia", age: 25, country: "Spain", }, { id: 3, picture: "/src/assets/img/three.jpg", firstName: "Yuki", lastName: "Tanaka", age: 28, country: "Japan", }, ]); const loadWoman = (id) => { activeWindows.value.push(id); }; </script>
问题分析
这是Vue中v-for和v-if同元素使用的优先级问题——Vue会先执行v-if,再执行v-for。当v-if判断时,v-for循环生成的index还未被创建,因此会报index未定义的错误。另外用index作为判断依据存在隐患:如果数组元素顺序变化,index会跟着改变,导致组件激活状态关联出错。
解决方案
推荐采用第二种更健壮的方案,同时提供基础修复方案供参考:
方案一:给v-for套外层容器,迁移v-if
用<template>标签包裹Contestant,将v-for放在<template>上,v-if保留在Contestant组件上,确保v-for先执行生成index后,v-if能正确访问:
<template> <div class="page" id="womensPage"> <div> <template v-for="(woman, index) in women" :key="index"> <Contestant :contestant="woman" v-if="activeWindows.includes(index)" /> </template> </div> <!-- 列表部分代码不变 --> </div> </template>
方案二:改用唯一id作为激活标识(推荐)
放弃用index,改用每个女性对象的唯一id管理激活状态,避免数组顺序变化导致的错误,同时优化重复添加的问题:
修正后的完整代码
<template> <div class="page" id="womensPage"> <div> <!-- 用woman.id判断是否激活 --> <Contestant v-for="woman in women" :key="woman.id" :contestant="woman" v-if="activeWindows.includes(woman.id)" /> </div> <div class="pageContent"> <h1 class="center fs26 fw800 red uc mb50">Females</h1> <div class="contestantListWrap"> <div class="womenList flex between"> <!-- 点击时传递woman.id --> <div class="woman mb10" v-for="woman in women" :key="woman.id" @click="loadWoman(woman.id)"> <img :src="woman.picture" class="womanImg" /> <h3 class="flex red jCenter">{{ woman.firstName }} {{ woman.lastName }}</h3> <p>Age {{ woman.age }}</p> <p>Country: {{ woman.country }}</p> </div> </div> </div> </div> </div> </template> <script setup> import { ref } from "vue"; import Contestant from "@/components/Contestant.vue"; const activeWindows = ref([]); const women = ref([ { id: 1, picture: "/src/assets/img/one.jpg", firstName: "Alice", lastName: "Smith", age: 30, country: "USA", }, { id: 2, picture: "/src/assets/img/two.jpg", firstName: "Maria", lastName: "Garcia", age: 25, country: "Spain", }, { id: 3, picture: "/src/assets/img/three.jpg", firstName: "Yuki", lastName: "Tanaka", age: 28, country: "Japan", }, ]); const loadWoman = (id) => { // 避免重复添加同一个组件 if (!activeWindows.value.includes(id)) { activeWindows.value.push(id); } }; </script>
优化说明
- 用
woman.id替代index,数组元素顺序变化不会影响激活状态的正确性 - 增加重复判断,防止同一个组件被多次添加到激活列表
v-for的key改用唯一id,符合Vue性能优化的最佳实践
内容的提问来源于stack exchange,提问作者crawlingdev
相关产品推荐
相关产品推荐

