【问题标题】:using watch function w/prop in Vue3 composition api在 Vue 3 组合 API 中使用带有/prop 的匹配函数
【发布时间】:2022-01-14 15:40:59
【问题描述】:

我有一个组件可以呈现一个库存计算机设备表。以下是初始渲染的相关代码:

let oEquiptByType = reactive({
  Laptop: [],
  iPad: [],
  "Document Camera": [],
  "Overhead Projector": [],
  Chromebook: [],
  Desktop: [],
  MacBook: [],
  Scanner: [],
});

// ======== Props =========== //
const props = defineProps({
  propFormData: {},
});

// Now let's use Stein to retrieve the SS data
// eslint-disable-next-line no-unused-vars
const fetchSheetsData = function () {
  const store = new SteinStore(
    "https://api.steinhq.com/v1/storages/618e81028d29ba2379044caa"
  );
  store
    .read("HS - Classrooms")
    .then((data) => {
      scrapDataHSClassrooms.value = data;
      emptyRowsRemoved.value.forEach((item) => {
        // Let's construct an object that separates equipment by type
        // Check if property exists on oEquiptByType object
        const exists = Object.prototype.hasOwnProperty.call(
          oEquiptByType,
          item["Equipment"]
        );

        // If item(row) is good lets push the row onto the corresponding Object Array
        // in oEquiptByType. This will construct an object where each object property corresponds
        // to an equipment category. And each oEquiptByType entry is an array where each array
        // element is a row from the SS. e.g., oEquiptByType["Laptop"][3] is a row from
        // SS and is a laptop.
        if (exists) {
          oEquiptByType[item["Equipment"]].push(item);
        }
      });
    })
    .catch((e) => {
      console.error(e);
      failure.value = true;
    });
};

// =============== Called on component mount =============================== //
onMounted(fetchSheetsData);

初始渲染很好。现在我在道具上有一个观察者,所以当有人为库存提交新项目时,我将该数据推送到相应的对象数组(即,笔记本电脑将被推送到 oEquiptByType[props.propFormData.Equipment] em> 通过 oEquiptByType[props.propFormData.Equipment].push(props.propFormData);

// ================================================================ //
// ======================= Watch effects ========================== //
// ================================================================ //
watch(props.propFormData, () => {
  // Push the submitted form item onto the reactive
  // oEquiptByType object array. This update of Vue state
  // will then be injected into DOM and automagically update browser display.
  oEquiptByType[props.propFormData.Equipment].push(props.propFormData);
});

这适用于我添加到后端的第一个项目,您可以在此处看到原始项目,然后添加第一个项目:

在添加第一项后(笔记本电脑)

请注意 oEquiptByType[props.propFormData.Equipment] 添加了新项目。伟大的。 但是现在当我添加第二个项目(MacBook)时,这是结果状态:

请注意 Macbook 阵列已更新,但笔记本电脑阵列的最后一项已被 Mac book 条目覆盖???对于从用户添加的任何其他项目,此行为将继续。我已经阅读了文档,没有看到任何可以解释这种行为的东西。我希望也许有超过我对 Vue 有限经验的人可以帮助我。需要任何其他信息,请告诉我。谢谢...

更新: 将 JSON.Stringify 放入 watch 函数中

更新二: 这是prop.FormData的血统-

我们从表单模式开始并发出如下表单数据:

emit("emiterUIUpdate", formAsPlainObject);

然后在父App.vue中捕获数据:

    <FormModal
        v-show="isModalVisible"
        @close="closeModal"
        @emiterUIUpdate="updateUI"
      >

  

<DisplayScrap :propFormData="formData" />

    const formData = reactive({});
    
// Method to be called when there is an emiterUIUpdate event emiited
// from form-modal.vue @param(data) is the form data sent from the
// form submission via the event bus. We will then send this data back
// down to child display-scrap component via a prop.
    const updateUI = (data) => {
      Object.assign(formData, data);
    };

然后正如之前在 display-scrap.vue 中发布的那样,prop propFormData 在 watch 函数中被定义和监视。希望有帮助..

【问题讨论】:

  • 如果没有更多关于 props.propFormData 的确切来源(当然,它来自父组件)以及对象是如何创建的信息,就无法回答。您很可能只是在重用单个对象实例。通过简单比较即可轻松测试Laptop[17] === Macbook[0]
  • 查看更新 2 以获得更多解释。

标签: vuejs3 vue-composition-api vue-reactivity


【解决方案1】:

手表的触发频率似乎比您预期的要高。

可能是props.propFormData 的更改是原子性的,每次增量更改都会触发道具更改,进而触发监视。

尝试使用JSON.stringify 在控制台记录props.propFormData 的值,以查看触发它的更改。

【讨论】:

  • 这也是我的想法,但请参阅上面的更新图像。观察者似乎每次提交只触发一次。
【解决方案2】:

这里发生了什么:

  1. 您的表单模式在 Ok 或 Save(按钮)上发出 emiterUIUpdate 事件
  2. Parent 获取发射的对象并使用Object.assing 将发射对象的所有属性复制到formData reactive 对象。而不是创建全新的对象,您只是一遍又一遍地替换该对象的所有属性的值
  3. formData 对象通过 prop 传递给子组件,每当它发生变化时,它就会被pushed 到目标数组

因此,您对同一对象有多个引用(formData 由父组件持有)并且所有这些引用都指向内存中的同一对象。每个Object.assign 都会覆盖该对象的属性,所有引用都会反映这些更改(因为所有引用指向内存中的同一个对象)

请注意,这与 Vue 反应性无关 - 这是简单的 JavaScript - value vs reference

对于该做什么没有明确的答案。有多种选择:

  1. 最简单(但不是最清楚)
  • 只是不要使用Object.assign - 每次单击“保存”时都会创建新对象
  • formData 更改为ref - const formData = ref({})
  • emiterUIUpdate 事件上替换ref 的值 - formData.value = { ...data }
  • 你在子进程中的watch 处理程序将停止工作because you are watching props in a wrong way - 而不是watch(props.propFormData, () =&gt; { 使用watch(() =&gt; props.propFormData, () =&gt; {
  1. 更好的解决方案
  • 数据应归父组件所有
  • 当 modal 发出新数据(保存)时,Parent 只会将新生成的对象添加到列表中
  • 使用道具与DisplayScraps组件共享数据(这可以是一个简单的列表或computed创建类似于oEquiptByType的对象)

【讨论】:

  • 我喜欢第二个选项,但不清楚将新对象添加到列表中是什么意思?你是什​​么意思父母应该拥有数据?谢谢。
猜你喜欢
  • 2021-01-14
  • 1970-01-01
  • 2021-05-30
  • 2021-05-10
  • 2022-11-05
  • 1970-01-01
  • 2020-06-07
  • 2021-10-28
  • 2021-04-22
相关资源
最近更新 更多