You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue删除列表最后一个元素后出现undefined错误求助

问题排查:Vue中使用splice删除列表最后一项时出现undefined错误

问题描述

这是我正在开发的大型组件的简化版本,遇到一个问题:使用splice删除列表最后一项时会出现undefined错误,触发场景如下:

  • 逐个删除元素时无错误,仅当删除最后一项且列表中至少存在一项在其前方时才会触发错误;
  • 移除v-for中的@click="handleProductrowTap(index)"后,错误不再出现。

代码示例

<template>
  <div v-for="(row, index) in this.rows" :key="index" v-bind:id="`row-${index}`"  @click="handleProductrowTap(index)">
    <div v-bind:id="`row-${index}`" v-bind:class="rowCssDecorations(row, index)">
      <span>{{ row.text }}</span> <button class="borderNormal" style="background-color: white; border:black;" @click.prevent="deleteRow(row, index)">Delete me </button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'TestRow',
  created() {},
  data() {
    return {
      rows: [
        {'id':1, text: 'Granny smith', type: 'apple'},
        {'id':2, text: 'Conference', type: 'pear'},
        {'id':3, text: 'Alpine', type: 'strawberry'},
      ],
      currentRowIndex: 0
    }
  },
  methods: {
    //returns css styling to every row in v-for. 
    //It sets color of the row according to the row.type and highlights selected row (if currentRowIndex == index). 
    rowCssDecorations(row) {
      var resultingCss = ''

      //change color according to rows.type
      switch (row.type) {
        case 'apple':
          resultingCss = resultingCss + 'appleClass'
          break
        case 'pear':
          resultingCss = resultingCss + 'pearClass'
          break
        case 'strawberry':
          resultingCss = resultingCss + 'strawberryClass'
          break

        default:
          resultingCss = resultingCss + 'defaultClass'
          break
      }

      //Hliglight the current row
      if (this.rows[this.currentRowIndex].id == row.id) {
        resultingCss = resultingCss + ' borderSelected '
        } else {
          resultingCss = resultingCss + ' borderNormal  '
        }

      return resultingCss
    },

    //deletes the row
    deleteRow(index) {
      this.rows.splice(index, 1)
    },

    //Simply sets the selected row id to currentRowIndex  .
    handleProductrowTap( index) {
      this.currentRowIndex = index
    },
  },
}
</script>
<style lang="scss" scoped>
.appleClass {
  background-color: greenyellow;
}
.pearClass {
  background-color: palegoldenrod;
}
.strawberryClass {
  background-color: red;
}
.defaultClass {
  background-color: gainsboro;
}
.borderSelected{
  border-color: black;
  border-width: 4px;
}
.borderNormal{
  border-color: black;
  border-width: 2px;
}
</style>

错误原因分析

错误的核心是**currentRowIndex保存的旧索引在删除最后一项后超出了数组的有效范围**:

  1. 点击最后一项时,handleProductrowTap会把currentRowIndex设为最后一项的索引(比如原数组有3项时,索引为2);
  2. 删除最后一项后,数组长度缩短,有效索引范围变小(比如变为0和1),但currentRowIndex仍为2;
  3. Vue重新渲染列表时,rowCssDecorations方法会尝试访问this.rows[this.currentRowIndex].id,此时this.rows[2]为undefined,访问它的id就会抛出错误。

移除v-for的@click后错误消失,是因为此时currentRowIndex始终是初始值0,删除最后一项后数组至少还有一项,this.rows[0]依然存在,不会触发undefined访问。

解决方案

方案一:删除后修正currentRowIndex的取值范围

在deleteRow方法中,删除元素后检查currentRowIndex是否超出当前数组的有效索引,若超出则调整为最后一个有效索引(数组为空时设为0):

deleteRow(index) {
  this.rows.splice(index, 1);
  // 调整选中索引到有效范围
  if (this.currentRowIndex >= this.rows.length) {
    this.currentRowIndex = this.rows.length > 0 ? this.rows.length - 1 : 0;
  }
}

方案二:用唯一ID代替索引跟踪选中项

索引会随数组增删变化,改用row的唯一id跟踪选中项更可靠:

  1. 修改data中的跟踪变量:
data() {
  return {
    rows: [
      {'id':1, text: 'Granny smith', type: 'apple'},
      {'id':2, text: 'Conference', type: 'pear'},
      {'id':3, text: 'Alpine', type: 'strawberry'},
    ],
    currentRowId: 1 // 初始选中第一个项的id
  }
}
  1. 修改点击事件方法和模板调用:
handleProductrowTap(row) {
  this.currentRowId = row.id;
}
<div v-for="(row, index) in rows" :key="index" :id="`row-${index}`" @click="handleProductrowTap(row)">
  1. 修改样式判断逻辑:
// 高亮当前选中行
if (this.currentRowId == row.id) {
  resultingCss += ' borderSelected ';
} else {
  resultingCss += ' borderNormal  ';
}

方案三:增加空值判断避免undefined访问

在rowCssDecorations方法中,先检查目标元素是否存在再访问其属性:

// 高亮当前选中行
const currentRow = this.rows[this.currentRowIndex];
if (currentRow && currentRow.id == row.id) {
  resultingCss += ' borderSelected ';
} else {
  resultingCss += ' borderNormal  ';
}

内容的提问来源于stack exchange,提问作者Janis Jakaitis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 05:05:24