VueJS TypeError: 读取undefined的toLowerCase属性问题排查
1. 数据类型不匹配引发报错
你的fruits数组原本存储的都是带msg属性的对象(比如{ msg: 'Monstera' }),但addPlants mutation里直接向数组push了字符串'Banana':
addPlants(state) { state.fruits.push('Banana') },
当filteredList getter执行筛选逻辑时,会遍历数组并尝试访问每个元素的msg属性:
return item.msg.toLowerCase().indexOf(...)
对于刚添加的字符串'Banana',item.msg是undefined,调用toLowerCase()自然会抛出Cannot read properties of undefined (reading 'toLowerCase')错误。
2. 添加元素触发筛选getter的原因
Vuex的getter是响应式依赖追踪的:组件模板中使用了filteredList,意味着组件依赖这个getter的返回值。当你通过addPlants修改state.fruits(数组是引用类型,push操作会改变原数组),Vuex会检测到state变化,自动重新计算所有依赖该state的getter,因此filteredList会被重新执行,进而触发报错。
步骤1:修正addPlants mutation,保持数据结构一致
把push的字符串改成符合原有结构的对象,同时支持接收组件传递的新植物名称:
addPlants(state, newPlantName) { state.fruits.push({ msg: newPlantName }) },
步骤2:修复组件的输入绑定问题
当前组件输入框直接绑定v-model="fruits",但fruits是Vuex state里的数组,直接绑定会违反单向数据流原则,还会导致意外修改state。改成绑定本地数据属性:
在组件中添加本地变量并封装添加方法:
export default { // ...其他代码 data() { return { newPlant: '' // 绑定输入框的本地变量 } }, methods: { ...mapMutations(['addPlants']), handleAddPlant() { if (this.newPlant.trim()) { this.addPlants(this.newPlant.trim()) this.newPlant = '' // 清空输入框 } } } }
修改模板中的输入和按钮:
<input v-model="newPlant" placeholder="输入植物名称..."> <button @click="handleAddPlant"> 添加新植物 </button>
步骤3:可选:给getter添加容错处理
为避免未来类似数据结构错误,在filteredList getter里添加判断,确保item.msg存在再执行后续逻辑:
filteredList (state) { return state.fruits.filter((item) => { if (typeof item === 'object' && item !== null && item.msg) { return item.msg.toLowerCase().indexOf(state.query.toLowerCase()) !== -1 } return false // 过滤掉不符合结构的元素 }) }
组件中v-for的index参数未定义,会导致删除操作出错,修改模板:
<div class="item fruit" v-for="(fruit, index) in filteredList" :key="fruit.msg"> <p>{{ fruit.msg }}</p> <button @click="deletePlants(index)"> 删除 </button> </div>
内容的提问来源于stack exchange,提问作者Micha

