实现编辑当前行内容并保存修改功能时,为什么数据传着传着就变了?

为什么数据传着传着变了?

页面结构

  • 组件A

    • 组件B1 => 用于设置过滤条件和进行过滤
    • 组件B2 => 表格,显示过滤的结果,表格中还有一个列可以用于操作对应行数据的编辑和删除
    • 组件B3 => 弹出编辑当前行的表单

我在实现编辑当前行内容并保存功能的时候遇到了些问题,以下是我的实现思路和问题。

在组件A中我声明了一个响应式数据,用于跟踪当前行的数据

const editingEquipmentLedgerForm = ref();
function setEditingEquipmentLedgerForm(newValue) {
  editingEquipmentLedgerForm.value = newValue;
}

还有一个方法用于处理点击编辑按钮事件,它将会被传给组件B(表格显示)

function handleEdit(idx, row) {
  // todo: 编辑当前行
  console.log("edit current row: ", idx, row);
  setEditingEquipmentLedgerForm({...toRaw(row)});
  console.log("handleEdit: editingEquipmentLedgerForm: ", editingEquipmentLedgerForm);
  // 打开编辑弹窗
  openEditEquipmentLedgerDrawer();
}

当前编辑的行数据会被传给组件B3(编辑弹窗),当我们打开编辑弹窗的时候,对应行的内容就显示在弹窗中的表单里了。

 <EditEquipmentLedgerDrawer
    :startLoading="startLoading"
    :endLoading="endLoading"
    :postEditedEquipmentLedger="postEditedEquipmentLedger"
    :editingEquipmentLedgerForm="editingEquipmentLedgerForm"
    :closeEditEquipmentLedgerDrawer="closeEditEquipmentLedgerDrawer"
    v-model:showEditEquipmentLedgerDrawer="showEditEquipmentLedgerDrawer"
  />

但是当我修改之后,点击保存的时候,就报错了。从我打印的数据可以看到当前修改行的数据变成undefined了。

async function saveEditedEquipmentLedger(formInstance) {
  // todo: 保存新的设备台账信息
  if (!formInstance) return;

  console.log("formInstance data: ", formInstance);
  await formInstance.validate(async (valid, fields) => {
    console.log("validate form: ", valid, fields);
    if (valid) {
      console.log("submit");
      // todo: 需要对表单数据进行处理

      try {
        console.log("editingEquipmentLedgerForm: ", editingEquipmentLedgerForm);
        postEditedEquipmentLedger(extractEditedEquipmentLedgerFormData(editingEquipmentLedgerForm));
        // 模拟: 上传加载,提示上传成功,然后关闭drawer
        startLoading();
        await sleep(1);
        endLoading();

        ElMessage({
          message: "设备台账修改成功,您现在可以点击查询获取最新的数据了!",
          type: "success",
          duration: 1000
        })

        closeEditEquipmentLedgerDrawer();
        formInstance.resetFields();
      } catch (error) {
        ElMessage({
          message: `设备台账修改失败 ${error.message}`,
          type: "error",
          duration: 1000
        })
      }

    } else {
      // 提示验证失败
      console.log("error submit", fields);
    }
  })

}

image.png

image.png

  • 为什么会出现这样的问题呢?该怎么解决呢?
  • 我实现编辑保存的方式合理吗?这个组件划分的合理吗?参数传递合理吗?是不是把简单问题变得复杂了?

———————————————————————
这个问题,我简化抽象了一下,相同的逻辑是可以成功保存的,我不知道我的问题出在了哪里?elementplus playground
附:

阅读 1.4k
2 个回答

这里给的默认值是undefined
image.png
所以子组件初始化接受的这个值就是undefined
image.png
因为你组件里的editingEquipmentLedgerForm和传过去的editingEquipmentLedgerForm没有关联关系,所以组件里的editingEquipmentLedgerForm没有随着父元素的editingEquipmentLedgerForm改变而改变。

你可以使用const props = defineProps(...);声明总的props,在需要的地方使用props.xxx取值。或者在EditEquipmentLedgerDrawer组件上使用v-if,在openEditEquipmentLedgerDrawer的时候在渲染这个组件


1、就像@陟上晴明 说的,不要直接修改props
2、尽量使用props.xxx的形式取值,因为它是动态的,解构出来的值是静态不变的

根据你简化版的案例给你走一下流程,看一下数据在哪,是什么
首先需要知道一点<script setup>里面的代码会转化成setup()函数进行执行,setup()会把顶层变量抛出,供组件使用

1、在script中打个断点,然后再控制台中找到当前的代码位置,看下setup转化后的结果是什么
image.png
这个setup就是,打印出来就是下面的代码(简化版),可以看出来没有editingItem, closeDrawer, simulateOperation这些props带过来的值(这里可能是关键,需要看下转换代码是什么逻辑),所以props中解构的值只在setup函数作用域中存在和使用且他们的值是固定的

setup(__props, { expose: __expose }) {
  __expose();

  const formRef = ref();
  const {editingItem, closeDrawer, simulateOperation} = __props;
  const data1 = ref(111)

  debugger
  async function saveEditedItem(formInstance) {
    console.log(editingItem, editingItem.id)
    closeDrawer();
  }
  function cancelEditeItem(formInstance) {
    console.log("取消编辑");
  }

  function test1(){
    debugger
    return editingItem
  }

const __returned__ = { formRef, data1, saveEditedItem, cancelEditeItem, test1, ref }
Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })
return __returned__
}

2、继续往下走,看下组件执行完毕后有哪些数据
image.png
3、点击编辑按钮后的数据
image.png
4、点击按钮后,看下执行script中的函数,数据是什么
image.png
到此,整个数据转化的流程应该很清楚明了了。如果想要明白具体原理,建议有时间看下源码。

没仔细看,提醒两个点

  1. console.log(obj) 的输出不是当时的值,而是在查看的时候 obj 的值(尤其是属性)。如果想输出当时的时候,用 console.log(JSON.stringify(obj)),先序列化出来再输出,或者只取其中部分属性(取到原始类型为止)输出
  2. 如果一个对象在使用的时候不是预期,有可能是在其他地方修改了。一般传递对象的时候,都是传递的对象的引用。既然是引用,那在某个地方修改了属性值,所以通过引用访问的属性都是修改后的。如果想不受影响,需要使用深拷贝(或一定深度的拷贝)
推荐问题
宣传栏