【问题标题】:Vue array computed via parameterized getter not reactive通过参数化 getter 计算的 Vue 数组不是反应式的
【发布时间】:2020-07-06 16:11:31
【问题描述】:

我有一个讨论对象列表,其中包含一个 cmets 数组,每个评论都可以包含一个回复数组。我以这种方式显示讨论:

<div v-for="comment in comments" v-bind:key="comment._id">
  <Comment :itemId="itemId" :comment="comment" />
  <Replies v-if="comment.replies.length > 0" :itemId="itemId" :comment="comment" />
</div>
<Button value="Load more" @clicked="loadMoreComments(itemId)" />

和回复:

<div v-for="reply in replies" v-bind:key="reply._id">
  <Comment :itemId="itemId" :comment="reply" />
</div>
<Button :value="Load more" @clicked="loadChild()"/>

如您所见,两者都使用相同的模式。它们在计算属性上有所不同:

computed: {
  comments() {
    return this.$store.getters.DISCUSSION.comments.map(id => this.$store.getters.GET_COMMENT(id));
  },
  replies() {
    return this.$store.getters.GET_REPLIES(this.comment).map(id => this.$store.getters.GET_COMMENT(id));
  },
},

当我点击 cmets 的加载更多按钮时,会出现新的 cmets。但是当我在回复中点击加载更多按钮时,虽然我可以在调试器中看到数组被放大,但没有显示新的回复。

Vuex 存储子模块:

state: () => ({
  discussion: {
    incomplete: true,
    comments: [],
  },
  comments: {},
}),

getters: {
  DISCUSSION: state => state.discussion,
  GET_COMMENT: state => id => state.comments[id],
  GET_REPLIES: state => (comment) => {
    if (comment.allShown) {
      return comment.replies;
    }
    return comment.replies.slice(0, REPLY_LIMIT);
  },
},
mutations: {
  APPEND_COMMENTS: (state, payload) => {
    const { comments, incomplete, userId } = payload;
    state.discussion.incomplete = incomplete;
    const commentIds = [];
    comments.forEach(comment => processComment(state, comment, commentIds, userId));
    state.discussion.comments = state.discussion.comments.concat(commentIds);
  },
  PREPEND_COMMENTS: (state, payload) => {
    const { comments, userId } = payload;
    const commentIds = [];
    comments.forEach(comment => processComment(state, comment, commentIds, userId));
    state.discussion.comments = commentIds.concat(state.discussion.comments);
  },
  SET_REPLIES: (state, payload) => {
    console.log('SET_REPLIES');
    const { commentId, replies, userId, replace } = payload;
    const comment = state.comments[commentId];
    if (!comment) {
      return;
    }

    state.comments[commentId].showAll = true;
    const commentIds = [];
    replies.forEach(reply => processComment(state, reply, commentIds, userId));
    if (!comment.replies || comment.replies.length === 0 || replace) {
      state.comments[commentId].replies = commentIds;
    } else {
      state.comments[commentId].replies = comment.replies.concat(commentIds);
    }
  },
}

function processComment(state, comment, commentIds, userId) {
  if (comment.replies) {
    const repliesIds = [];
    comment.replies.forEach((reply) => {
      reply.voted = hasVoted(reply.votes, userId);
      state.comments[reply._id] = reply;
      repliesIds.push(reply._id);
    });
    comment.replies = repliesIds;
    comment.allShown = comment.replies.length < REPLY_LIMIT;
  } else if (!comment.parentId) {
    comment.replies = [];
    comment.allShown = false;
  }
  state.comments[comment._id] = comment;
  commentIds.push(comment._id);
}

完整的源代码在那里:https://github.com/literakl/mezinamiridici/tree/comment_refactoring/spa

这里是最小可重现代码框:https://codesandbox.io/s/frosty-taussig-v8u4b?file=/src/module.js

我已经证实这是因为带有参数的 getter。当我将回复放入静态数组以便我可以使用无参数 getter 时,它开始工作。

我遵循这个建议:https://forum.vuejs.org/t/vuex-best-practices-for-complex-objects/10143

问题出在哪里?

更新:

有一点味道是突变GET_REPLIES,因为它适用于传递的对象。所以Vue没有机会检测到对象来自状态。所以我重写了它只传递commentId并从状态加载评论,但它没有帮助。

【问题讨论】:

    标签: javascript vue.js vuex


    【解决方案1】:

    我是客人,您应该将 showAll 替换为 allShown 道具,并使用 Vue.setcomments 对象添加新键,因为由于 Vue 警告,Vuex 没有看到新道具,请参阅 caveats for objects

        SET_REPLIES: (state, payload) => {
          console.log("SET_REPLIES");
          const { commentId, replies, userId, replace } = payload;
          const comment = state.comments[commentId];
          if (!comment) {
            console.log(`Comment ${commentId} not found`);
            return;
          }
    
          state.comments[commentId].allShown = true;
          // state.comments[commentId].showAll = true;
    
    ...
    
    function processComment(state, comment, commentIds, userId) {
      if (comment.replies) {
        const repliesIds = [];
        comment.replies.forEach(reply => {
          Vue.set(state.comments, reply._id, reply);
          // state.comments[reply._id] = reply;
          repliesIds.push(reply._id);
        });
        comment.replies = repliesIds;
        comment.allShown = comment.replies.length < 3;
      } else if (!comment.parentId) {
        comment.replies = [];
        comment.allShown = false;
      }
      Vue.set(state.comments, comment._id, comment);
      // state.comments[comment._id] = comment;
      commentIds.push(comment._id);
    }
    

    还可以像这样正确调用 GET_REPLIES:

      computed: {
        replies() {
          return this.$store.getters
            .GET_REPLIES(this.comment) // passing comment itself instead of its id
            .map(id => this.$store.getters.GET_COMMENT(id));
        }
      },
    

    corrected example

    【讨论】:

    • 您好,我也发现了那个错字,但没有帮助。您的沙箱无法正常工作,“无法读取未定义的属性‘切片’”。
    • 正确的 GET_REPLIES 调用,正如我在答案末尾提到的那样
    • Vue.set(state.cmets, comment._id, comment);是游戏规则的改变者,谢谢!
    猜你喜欢
    • 2021-02-11
    • 2021-11-22
    • 2020-12-01
    • 2019-11-18
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    • 2020-05-26
    • 1970-01-01
    相关资源
    最近更新 更多