当前位置: 首页 > article >正文

reactive数据修改无效

环境

vue:3.2.13

element-plus: 2.9.6

typescript:4.5.5

问题

表格列表页面,页面中有新增和修改操作,新增和修改共用一个弹窗,弹窗中表单绑定的数据修改无效。复现步骤是先点击表格中的修改,然后点击新增,此时弹窗中表单数据没有置空(有赋值为空的操作)。

问题代码

代码只是为了复现问题,有类型标注缺失或者其他小问题

<template>
  <div class="home">
    <el-form :inline="true" :model="dataForm" @keyup.enter="getDataList()">
      <el-form-item>
        <el-input v-model="dataForm.name" placeholder="名称" clearable></el-input>
      </el-form-item>
      <el-form-item>
        <el-button @click="getDataList()">查询</el-button>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="addOrUpdateHandle()">新增</el-button>
      </el-form-item>
    </el-form>
    <el-table v-loading="loading" :data="dataList" border style="width: 100%">
      <el-table-column prop="name" label="名称" header-align="center" align="center"></el-table-column>
      <el-table-column prop="createDate" label="创建时间" show-overflow-tooltip header-align="center" align="center"></el-table-column>
      <el-table-column prop="creatorName" label="创建者" header-align="center" align="center"></el-table-column>
      <el-table-column label="操作" fixed="right" header-align="center" align="center" width="248">
        <template v-slot="scope">
          <el-button type="primary" link @click="addOrUpdateHandle(scope.row)">修改</el-button>
        </template>
      </el-table-column>
    </el-table>
    <add-or-update ref="addOrUpdateRef" @refreshDataList="getDataList"></add-or-update>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import AddOrUpdate from './add-or-update.vue';
const dataForm = ref({
  name: '',
});
const dataList = ref([]);
const loading = ref(false);
// 模拟接口数据
function generate() {
  const arr = [];
  for (let index = 0; index < 8; index++) {
    arr.push({
      id: index + 1,
      name: `name${index}`,
      createDate: `2025-3-${index + 1}`,
      creatorName: 'admin',
    });
  }
  return arr;
}
// 模拟接口请求
const getDataList = () => {
  loading.value = true;
  setTimeout(() => {
    dataList.value = generate();
    loading.value = false;
  }, 1500);
};
onMounted(() => {
  getDataList();
});

// 新增或者修改弹窗
const addOrUpdateRef = ref();
const addOrUpdateHandle = ({ id, name }: { id?: string; name?: string } = {}) => {
  addOrUpdateRef.value.init(id, name);
};
</script>
<template>
  <el-dialog v-model="visible" :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :close-on-press-escape="false">
    <el-form :model="dataForm" :rules="rules" ref="dataFormRef" @keyup.enter="dataFormSubmitHandle()">
      <el-form-item prop="name" label="名称">
        <el-input v-model="dataForm.name" placeholder="名称"></el-input>
      </el-form-item>
    </el-form>
    <template v-slot:footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="dataFormSubmitHandle()">确定</el-button>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['refreshDataList']);
const visible = ref(false);
const dataFormRef = ref();
// 表单数据
const dataForm = reactive({
  id: '',
  name: '',
});
// 表单规则
const rules = ref({
  name: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
});
// 初始化
const init = (id?: string, name?: string) => {
  visible.value = true;
  dataForm.id = '';
  dataForm.name = '';
  // 重置表单数据
  if (dataFormRef.value) {
    dataFormRef.value.resetFields();
  }
  if (id && name) {
    getInfo(id, name);
  }
};

// 获取信息,因为信息很少,并且表格行中有数据,所以没有请求接口
const getInfo = (id: string, name: string) => {
  Object.assign(dataForm, {
    id,
    name,
  });
};

// 表单提交
const dataFormSubmitHandle = () => {
  dataFormRef.value.validate((valid: boolean) => {
    if (!valid) {
      return false;
    }
    // 模拟接口请求
    Promise.resolve(true).then((res) => {
      ElMessage.success({
        message: '成功',
        duration: 500,
        onClose: () => {
          visible.value = false;
          emit('refreshDataList');
        },
      });
    });
  });
};

defineExpose({
  init,
});
</script>

<style lang="less"></style>

多操作几次可以发现,如果第一次点击的是新增,那么不会出现问题;如果第一次点击的是修改,那么点击新增时,名称始终是第一次点击修改那行的名称。

分析

遇到不明白原因的问题,可以采取很多种方法,之前的博文分析中有使用过,但是没仔细说过。vue3自定义hooks遇到的问题中使用的是打印关键数据分析;安装react报错中使用了查看报错信息分析原因;antd的表格组件错乱问题中使用了查找仓库Issues和根据问题现象审查元素分析问题;还有之前没用过的注释排除法,把感觉有问题的代码区域(不好确定就尽量多)注释掉,一点一点缩小范围来排查出问题代码;当然还有使用更频繁的方法百度,现在AI这么流行的情况下,使用AI搜索更方便。

本次采用打印关键数据分析

<template>
  <el-dialog v-model="visible" :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :close-on-press-escape="false">
    <el-form :model="dataForm" :rules="rules" ref="dataFormRef" @keyup.enter="dataFormSubmitHandle()">
      <el-form-item prop="name" label="名称">
        <el-input v-model="dataForm.name" placeholder="名称"></el-input>
      </el-form-item>
    </el-form>
    <template v-slot:footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="dataFormSubmitHandle()">确定</el-button>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['refreshDataList']);
const visible = ref(false);
const dataFormRef = ref();
// 表单数据
const dataForm = reactive({
  id: '',
  name: '',
});
// 表单规则
const rules = ref({
  name: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
});
// 初始化
const init = (id?: string, name?: string) => {
  visible.value = true;
  dataForm.id = '';
  dataForm.name = '';
  console.log(dataForm);
  // 重置表单数据
  if (dataFormRef.value) {
    dataFormRef.value.resetFields();
  }
  console.log(dataForm);
  if (id && name) {
    getInfo(id, name);
  }
  console.log(dataForm);
};

// 获取信息,因为信息很少,并且表格行中有数据,所以没有请求接口
const getInfo = (id: string, name: string) => {
  Object.assign(dataForm, {
    id,
    name,
  });
};

// 表单提交
const dataFormSubmitHandle = () => {
  dataFormRef.value.validate((valid: boolean) => {
    if (!valid) {
      return false;
    }
    // 模拟接口请求
    Promise.resolve(true).then((res) => {
      ElMessage.success({
        message: '成功',
        duration: 500,
        onClose: () => {
          visible.value = false;
          emit('refreshDataList');
        },
      });
    });
  });
};

defineExpose({
  init,
});
</script>

<style lang="less"></style>
修改点击1修改点击2修改点击3
打印1{“id”: “”, “name”: “”}{“id”: “”, “name”: “”}{“id”: “”, “name”: “”}
打印2{“id”: “”, “name”: “”}{“id”: “”, “name”: “name0”}{“id”: “”, “name”: “name0”}
打印3{“id”: 1, “name”: “name0”}{“id”: 7, “name”: “name6”}{“id”: 4, “name”: “name3”}

不要展开查看[[target]],因为这样都是一样的

从打印内容可以对比分析出问题是由代码dataFormRef.value.resetFields()导致的,那么直接注释掉这里,再次尝试看看,可以看到现在不会出现之前问题了,但是出现了新问题,表单校验状态没重置(添加代码dataFormRef.value.resetFields()也是为了解决表单状态重置的)。现在找到问题之后,我们尝试进行修改。

方法一

除了使用表单方法进行重置之外,还可以关闭弹窗进行销毁,在el-dialog上增加destroy-on-close,此方法可以解决问题,但是会产生额外DOM操作性能,不推荐

<template>
  <el-dialog v-model="visible" :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :close-on-press-escape="false" destroy-on-close>
    <el-form :model="dataForm" :rules="rules" ref="dataFormRef" @keyup.enter="dataFormSubmitHandle()">
      <el-form-item prop="name" label="名称">
        <el-input v-model="dataForm.name" placeholder="名称"></el-input>
      </el-form-item>
    </el-form>
    <template v-slot:footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="dataFormSubmitHandle()">确定</el-button>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['refreshDataList']);
const visible = ref(false);
const dataFormRef = ref();
// 表单数据
const dataForm = reactive({
  id: '',
  name: '',
});
// 表单规则
const rules = ref({
  name: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
});
// 初始化
const init = (id?: string, name?: string) => {
  visible.value = true;
  dataForm.id = '';
  dataForm.name = '';
  // 重置表单数据
  // if (dataFormRef.value) {
  //   dataFormRef.value.resetFields();
  // }
  if (id && name) {
    getInfo(id, name);
  }
};

// 获取信息,因为信息很少,并且表格行中有数据,所以没有请求接口
const getInfo = (id: string, name: string) => {
  Object.assign(dataForm, {
    id,
    name,
  });
};

// 表单提交
const dataFormSubmitHandle = () => {
  dataFormRef.value.validate((valid: boolean) => {
    if (!valid) {
      return false;
    }
    // 模拟接口请求
    Promise.resolve(true).then((res) => {
      ElMessage.success({
        message: '成功',
        duration: 500,
        onClose: () => {
          visible.value = false;
          emit('refreshDataList');
        },
      });
    });
  });
};

defineExpose({
  init,
});
</script>

<style lang="less"></style>
方法二

把代码dataFormRef.value.resetFields()位置提前,放到所有重置表单数据前面

<template>
  <el-dialog v-model="visible" :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :close-on-press-escape="false">
    <el-form :model="dataForm" :rules="rules" ref="dataFormRef" @keyup.enter="dataFormSubmitHandle()">
      <el-form-item prop="name" label="名称">
        <el-input v-model="dataForm.name" placeholder="名称"></el-input>
      </el-form-item>
    </el-form>
    <template v-slot:footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="dataFormSubmitHandle()">确定</el-button>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['refreshDataList']);
const visible = ref(false);
const dataFormRef = ref();
// 表单数据
const dataForm = reactive({
  id: '',
  name: '',
});
// 表单规则
const rules = ref({
  name: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
});
// 初始化
const init = (id?: string, name?: string) => {
  visible.value = true;
  // 重置表单数据
  if (dataFormRef.value) {
    dataFormRef.value.resetFields();
  }
  dataForm.id = '';
  dataForm.name = '';
  if (id && name) {
    getInfo(id, name);
  }
};

// 获取信息,因为信息很少,并且表格行中有数据,所以没有请求接口
const getInfo = (id: string, name: string) => {
  Object.assign(dataForm, {
    id,
    name,
  });
};

// 表单提交
const dataFormSubmitHandle = () => {
  dataFormRef.value.validate((valid: boolean) => {
    if (!valid) {
      return false;
    }
    // 模拟接口请求
    Promise.resolve(true).then((res) => {
      ElMessage.success({
        message: '成功',
        duration: 500,
        onClose: () => {
          visible.value = false;
          emit('refreshDataList');
        },
      });
    });
  });
};

defineExpose({
  init,
});
</script>

<style lang="less"></style>
方法三

为什么一般人不悔遇到这个问题呢?因为通常情况下,修改的时候都是通过请求详情接口进行表单数据初始化的,所以这个方法就是为了延缓(异步)修改时数据赋值时间。

<template>
  <el-dialog v-model="visible" :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :close-on-press-escape="false">
    <el-form :model="dataForm" :rules="rules" ref="dataFormRef" @keyup.enter="dataFormSubmitHandle()">
      <el-form-item prop="name" label="名称">
        <el-input v-model="dataForm.name" placeholder="名称"></el-input>
      </el-form-item>
    </el-form>
    <template v-slot:footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="dataFormSubmitHandle()">确定</el-button>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { reactive, ref, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['refreshDataList']);
const visible = ref(false);
const dataFormRef = ref();
// 表单数据
const dataForm = reactive({
  id: '',
  name: '',
});
// 表单规则
const rules = ref({
  name: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
});
// 初始化
const init = (id?: string, name?: string) => {
  visible.value = true;
  dataForm.id = '';
  dataForm.name = '';
  // 重置表单数据
  if (dataFormRef.value) {
    dataFormRef.value.resetFields();
  }
  if (id && name) {
    nextTick(() => getInfo(id, name));
  }
};

// 获取信息,因为信息很少,并且表格行中有数据,所以没有请求接口
const getInfo = (id: string, name: string) => {
  Object.assign(dataForm, {
    id,
    name,
  });
};

// 表单提交
const dataFormSubmitHandle = () => {
  dataFormRef.value.validate((valid: boolean) => {
    if (!valid) {
      return false;
    }
    // 模拟接口请求
    Promise.resolve(true).then((res) => {
      ElMessage.success({
        message: '成功',
        duration: 500,
        onClose: () => {
          visible.value = false;
          emit('refreshDataList');
        },
      });
    });
  });
};

defineExpose({
  init,
});
</script>

<style lang="less"></style>

为什么修改时候延迟(异步)初始化可以呢?可以再打印看一下

<template>
  <el-dialog v-model="visible" :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :close-on-press-escape="false">
    <el-form :model="dataForm" :rules="rules" ref="dataFormRef" @keyup.enter="dataFormSubmitHandle()">
      <el-form-item prop="name" label="名称">
        <el-input v-model="dataForm.name" placeholder="名称"></el-input>
      </el-form-item>
    </el-form>
    <template v-slot:footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="dataFormSubmitHandle()">确定</el-button>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { reactive, ref, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['refreshDataList']);
const visible = ref(false);
const dataFormRef = ref();
// 表单数据
const dataForm = reactive({
  id: '',
  name: '',
});
// 表单规则
const rules = ref({
  name: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
});
// 初始化
const init = (id?: string, name?: string) => {
  visible.value = true;
  dataForm.id = '';
  dataForm.name = '';
  console.log(dataForm);
  // 重置表单数据
  if (dataFormRef.value) {
    dataFormRef.value.resetFields();
  }
  console.log(dataForm);
  if (id && name) {
    nextTick(() => getInfo(id, name));
  }
};

// 获取信息,因为信息很少,并且表格行中有数据,所以没有请求接口
const getInfo = (id: string, name: string) => {
  Object.assign(dataForm, {
    id,
    name,
  });
  console.log(dataForm);
};

// 表单提交
const dataFormSubmitHandle = () => {
  dataFormRef.value.validate((valid: boolean) => {
    if (!valid) {
      return false;
    }
    // 模拟接口请求
    Promise.resolve(true).then((res) => {
      ElMessage.success({
        message: '成功',
        duration: 500,
        onClose: () => {
          visible.value = false;
          emit('refreshDataList');
        },
      });
    });
  });
};

defineExpose({
  init,
});
</script>

<style lang="less"></style>
修改点击1修改点击2修改点击3
打印1{“id”: “”, “name”: “”}{“id”: “”, “name”: “”}{“id”: “”, “name”: “”}
打印2{“id”: “”, “name”: “”}{“id”: “”, “name”: “”}{“id”: “”, “name”: “”}
打印3{“id”: 1, “name”: “name0”}{“id”: 7, “name”: “name6”}{“id”: 4, “name”: “name3”}

可以看出延迟(异步)初始化之后,dataFormRef.value.resetFields()重置之后不会再把name赋值为第一次点击修改的name值,因此可以猜测到表单的初始值(resetFields()方法作用是把表单数据修改为初始值,并移除表单状态)是第一次初始化的同步代码的值(getInfo是同步修改的值时出现问题了),同步代码中相应字段(表单项prop指定的)没值,表单执行resetFields()方法时就会是没值。

项目地址

问题代码:https://gitee.com/lydxwj/vue-reactive/tree/error

方法一:https://gitee.com/lydxwj/vue-reactive/tree/method1/

方法二:https://gitee.com/lydxwj/vue-reactive/tree/method2/

方法三:https://gitee.com/lydxwj/vue-reactive/tree/method3/

分析原因:https://gitee.com/lydxwj/vue-reactive


http://www.kler.cn/a/591051.html

相关文章:

  • Visual Studio2022 中的键盘注释快捷方式
  • R语言绘图 | 环状柱状图+散点柱状组合图绘制
  • Spring中的循环依赖问题是什么?
  • 五种方案实现双链路可靠数据传输
  • 顺序表的C语言实现与解析
  • MySQL事务介绍
  • Vala编程语言教程-运算符
  • 算法刷题记录——LeetCode篇(3) [第201~300题](持续更新)
  • 蓝桥杯16
  • HTML 样式与布局初体验:学习进程中的关键节点(二)
  • 【每日论文】New Trends for Modern Machine Translation with Large Reasoning Models
  • 详细介绍HFONT结构体
  • 科技资讯杂志科技资讯编辑部科技资讯杂志社2025年第2期目录
  • 出租车数据可视化分析-大数据-实训大作业
  • [HelloCTF]PHPinclude-labs超详细WP-Level 5-http协议-2
  • Doris vs Elasticsearch:全维度对比与实际成本案例解析
  • Linux驱动开发实战(五):Qt应用程序点RGB灯(保姆级快速入门!)
  • 2000-2019年各省地方财政税收收入数据
  • 【HCIA-晴天老师】15-VLAN的Hybrid配置笔记
  • LeetCode 78.子集