我有一个列表,一个包含评论数组的讨论对象,每个评论可以保存一个回复数组。我以这种方式显示讨论:
<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));
},
},
当我点击"加载更多"按钮以获取评论时,会出现新评论。但是当我在回复中点击"加载更多"按钮时,尽管我可以在调试器中看到数组已放大,但没有显示新回复。
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 并从状态加载注释,但它没有帮助。
我的客人应该用allShown
prop 替换showAll
,并使用Vue.set
向对象添加新键comments
因为由于 Vue 警告 Vuex 看不到新的道具,请参阅对象的注意事项
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));
}
},
更正的示例