【问题标题】:Losing Vue reactivity when passing object from Vuex Array between components在组件之间从 Vuex 数组传递对象时失去 Vue 反应性
【发布时间】:2021-10-04 10:30:14
【问题描述】:

我的 Main.vue 中有两个子组件:

  • 所有 cmets 的列表视图 (list-cmets)
  • 单个项目视图的详细视图(列表评论)

根据 currentListView 的值,它要么显示列表,要么显示详细视图

Main.vue:这里是列表/详细视图的子组件:

<template>
  <div id="list" v-if="enableListDrawer">
    <list-comments
      v-if="currentListView == 'comments'"
      :project="project"
      @showComment="showComment"
    />
    <list-comment
      v-else-if="currentListView == 'comment'"
      :selectedComment="selectedComment"
      @submitConversation="submitConversation"
    />
  </div>
</template>
<script>
export default {
  data() {
    return {
      currentListView: "comments",
      selectedComment: {},
    };
  },
  computed: {
    projects() {
      return this.$store.getters.projects.filter(
        (o) => o._id == this.currentProjectToEdit
      );
    },
    project() {
      return this.$store.getters.projects
        .filter((o) => o._id == this.currentProjectToEdit)[0]
        .pages.filter((page) => page.url == window.location.href);
    },
  },
  methods: {
    showComment(comment) {
      this.selectedComment = comment;
      this.currentListView = "comment";
    },
  },
};
</script>

在 list-cmets 组件中,我正在迭代项目数组中的项目。当用户单击列表中的一条评论时,组件会向 Main.vue 发出一个事件 showComment(并从数组中传递一个项目对象)。

在 Main.vue 中,emit 触发 showComment,我将传递的对象设置为数据变量 selectedComment(我猜这是 Vue 反应性丢失的地方)。 selectedComment 绑定到列表评论组件,该组件被激活以显示详细视图。

此时,详细视图中的单个项目对象不再具有反应性。

详细视图组件,我在其中显示一条评论:

在详细视图中没有什么特别的事情发生。我不是直接修改对象

<template>
  <div id="list-comment">
      ...
  </div>
</template>

<script>

import axios from "axios";

export default {
  components: {},
  data() {
    return {
      newReply: "",
    };
  },
  props: {
    selectedComment: {
      type: Object,
      required: true,
    },
  },
  methods: {
    deleteConversation(id) {
      this.$store.dispatch("deleteConversation", {
        commentId: this.selectedComment._id,
        conversationId: id,
        currentProjectToEdit: "",
      });
    },
    onSubmitConversation() {
      if (this.newReply.length) {
        this.$emit("submitConversation", {
          pageId: "",
          commentId: this.selectedComment._id,
          reply: this.newReply,
        });
      }
    },
  },
};
</script>

列表视图组件,可以选择一个项目

<template>
  <div id="list-comment">
    <div
      id="list-card"
      class="list-card-hover"
      v-for="(item, index) in project[0].comments"
      :key="index"
      @click="showComment(item)"
    >
      <div>{{ item.comment }}</div>
    </div>
  </div>
</template>

<script>
export default {
  components: {},
  data() {
    return {};
  },
  props: {
    project: {
      type: Array,
      required: true,
    },
  },
  methods: {
    showComment(item) {
      this.$emit("showComment", item);
    },
  },
};
</script>

如何保持 Vue-App 的反应性?有什么更好的方法?

【问题讨论】:

  • 您是否尝试过使用 vue devtools 来确保计算的属性在 currentListView(第一个 sn-p)中更新?
  • 所有计算属性都会相应更新。只是带有selectedComment 的详细视图不是响应式的。我假设selectedComment 的反应性丢失了,因为我在从list-comments 发出事件后将其分配给数据属性。但是我怎么能以另一种更好的方式将它传递给list-comment?实现这一点可能有更好的模式。
  • 我有一个理论,我将把它作为答案发布,如果不正确请告诉我

标签: javascript vue.js vue-component vuex vue-props


【解决方案1】:

详细视图使用索引号作为键,从而剥夺了 vue 的反应性。由于密钥未更新因此触发更新。

首先让我们假设project[0].comments 中的每条评论都有一个id 或某种唯一值。

我们不会将key 绑定到索引,而是将其绑定到唯一值。

<div
  id="list-card"
  class="list-card-hover"
  v-for="item in project[0].comments"
  :key="index.id"
  @click="showComment(item)"
>
      <div>{{ item.comment }}</div>
</div>

如果我们没有可以绑定的唯一键,有两种选择:

  1. 创建一个(这可能需要在服务器端,具体取决于您的用例)
  2. 我们可以观察道具数据的变化,并强制重新渲染

显然,#1 是比 2 号更好的解决方案,但为了演示,这就是我们将如何实现它。

<template>
  <div id="list-comment" :key="updateCount">
    <div
      id="list-card"
      class="list-card-hover"
      v-for="(item, index) in project[0].comments"
      :key="index"
      @click="showComment(item)"
    >
      <div>{{ item.comment }}</div>
    </div>
  </div>
</template>

<script>
export default {
  components: {},
  data() {
    return {
        updateCount:0,
    };
  },
  props: {
    project: {
      type: Array,
      required: true,
    },
  },
  watch:{
    project:{
        deep:true,
        handler(){
            this.updateCount++;
        }
    }
  },
  methods: {
    showComment(item) {
      this.$emit("showComment", item);
    },
  },
};
</script>

另外,我假设项目有一个唯一 ID,您可以将其作为道具传递,并使用该唯一 ID 作为键。

【讨论】:

  • 我已经通过在列表视图中将他的索引更改为 item._id 来尝试您的方法。也不行。您正在引用列表视图组件。实际上,在列表视图中,一切都是被动的。 (对不起,可能是我的错:我在代码 sn-ps 之前添加了标题以使这一点更清楚)。问题出在细节视图组件中,我通过事先将对象分配给数据变量来传递对象。我想这就是问题所在。根据这篇文章,这可能是失去反应性的原因:michaelnthiessen.com/…
【解决方案2】:

我想我现在找到了解决方案 - 也许有更好的模式,但我现在基本上所做的是,从列表视图中我发出一个仅包含对象 _id 的事件。

列表视图组件:

<div
   id="webcheck-guru-list-card"
   class="webcheck-guru-list-card-hover"
   v-for="item in project[0].comments"
   :key="item._id"
   @click="showComment(item._id)"
>

在我的 Main.vue 文件中,我将此 _id 存储在 selectedComment 变量中:

showComment(comment) {
   this.selectedComment = comment;
   this.currentListView = "comment";
},

然后我创建了一个新的计算属性,它从我的 (Vuex) 数组中返回选定的注释(基于 _id):

getSelectedComment() {
   if (this.project.length && this.selectedComment.length) {
     return this.project[0].comments.filter(
       (comment) => comment._id == this.selectedComment
     );
   } else {
     return [];
   }
},

这个新的计算属性绑定到我的详细视图组件。这样,一切都是被动的。

<list-comment
   v-else-if="currentListView == 'comment'"
   :selectedComment="getSelectedComment"
   @submitConversation="submitConversation"
/>

也许 Vue 中对此有更好的解决方案/模式 - 我认为我可以轻松地传递对象而不会失去反应性。

很高兴有人能提出更好的方法!

【讨论】:

    猜你喜欢
    • 2020-10-12
    • 2020-06-10
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    • 1970-01-01
    • 2018-11-29
    相关资源
    最近更新 更多