博客评论层级回复前后端实现(vue+springboot)
致谢首先感谢下面三个博主提供是思路:https://blog.csdn.net/qq_39125684/article/details/90679610https://www.cnblogs.com/fei-H/p/11359028.htmlhttps://www.cnblogs.com/godlovesme/p/10708358.html#commentform我的项目是前后端分离的,后台给前端
自己也搭建了个新的博客,部分文章与csdn同步.本文在我的新博客同样可以看到:
http://spzgczyj.top/blog/article.html?articleId=350
致谢
首先感谢下面三个博主提供是思路:
Vue 递归组件_任重道远-CSDN博客_vue中什么是递归组件
评论系统数据库设计及实现 - Mario_Xue - 博客园
我的项目是前后端分离的,后台给前端带有层级的json格式的文件,前端用vue渲染。这时候就出现了两个问题:
1. 前端vue渲染层级的回复需要递归,而我当时只会简单的v-for。
2. 后台数据库设计如何在3nf范式的前提下实现评论层级回复的存储,如果要查询出来,sql应该如何写,这搞搞就变成了SQL递归查询的问题,但是我们其实就不是很喜欢查询的时候用到SQL递归查询,搞搞就变得复杂了。一旦递归查询,效率就会很慢时间复杂度就和层数挂钩了。
后端实现
针对这两个问题,我先找到了后台的实现,我从评论系统数据库设计及实现 - Mario_Xue - 博客园 这位博主的文章找到了思路,
我的评论表字段是这样的:
不需要多余的表,一张表就够了。
这是我的实体类:
package com.gkd.blog.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.lang.NonNull;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.Date;
import java.util.List;
/**
* <p>description: 评论和该评论的用户信息 </p>
* <p>create: 2020/1/10 12:15</p>
* @author zhaoyijie
* @version v1.0
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CommentAndUser {
/**
* 评论id
*/
private Integer id;
/**
* 文章id
*/
private Integer articleId;
/**
* 游客的昵称
*/
@NotBlank
private String visitorName;
/**
* 游客的邮箱
*/
@NonNull
@Pattern(regexp= "^[A-Za-z]{1,40}@[A-Za-z0-9]{1,40}\\\\.[A-Za-z]{2,3}$")
private String userEmaill;
/**
* 评论日期
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date addTime;
/**
* 评论内容
*/
@NotBlank
private String commentContent;
/**
* 评论父id
*/
private Integer parentId;
/**
* 子评论列表,回复评论
*/
private List<CommentAndUser> childList;
}
针对这个表和实体类,先查询数据出来再组装数据:
List<CommentAndUser> allList = commentMapper.findAllCommentByArticleId(articleId);
Map<Integer, CommentAndUser> map = new HashMap<>();
List<CommentAndUser> result = new ArrayList<>();
for (CommentAndUser c : allList) {
if (c.getParentId() == null) {
result.add(c);
}
map.put(c.getId(), c);
}
for (CommentAndUser c : allList) {
if (c.getParentId() != null) {
CommentAndUser parent = map.get(c.getParentId());
if (parent == null) {
continue;
}
if (parent.getId().intValue() == c.getId().intValue()) {
continue;
}
if (parent.getChildList() == null) {
parent.setChildList(new ArrayList<>());
}
parent.getChildList().add(c);
}
}
这里result是最后返回给前端的数据列表,当然需要分页后再写成json返回给前端,这里时间复杂度就O(n)。而不是递归那种O(n^2)了。这里需要说明的是在遍历allList的时候,我们需要判断一下中的parent是否为null,理论上这个不应该需要考虑,因为子评论有父id,那肯定是找得到父评论的,但是,如果数据被人为不小心或者故意删除了父评论,但是子评论还携带父id,这样子map取出来就是null,null操作就会报错,所以为了保险,还是得先判断从map取出来的父评论对象是否为空。接着还得判断一下父评论的id是否等于子评论的id,这是一个坑,虽然正常操作下这种情况是不可能出现的,但是一旦数据库存着这种错误的数据,子评论的父id居然是自己,那么最后写成json会递归调用,自己调用自己最后一定会栈溢出。所以必须得加个保险。
前端实现
针对前端的渲染,我选用了vue.js。主要是我前端并不是很会,然后找了很多篇文章,发现都是.vue文件的,我并不喜欢,不是import 就是 exports。拜托,我只会简单的v-for和new vue的方式,找了好久找不到我想要的,然后我找到了这两篇博主的文章:
Vue 递归组件_任重道远-CSDN博客_vue中什么是递归组件
最后整合了一下把前端代码写了出来,这里只放关键代码
<div id="app"><list :list="commentAndUserList"></list></div>
<script>
function replyComment(id){
//回复评论的方法,根据自己的业务需求更改
}
Vue.component('List', {
name: 'list',
props: {
list: Array
},
template: `
<div><ul class="comments-ul" v-for="(CommentAndUser,index) in list">
<li class="usercomment">
<div class="fn__clear">
<div class="item__name" v-text="CommentAndUser.visitorName"></div>
</div>
<div class="vditor-reset">
<p v-text="CommentAndUser.commentContent"></p>
</div>
<div class="item__meta fn__clear">
<time v-text="CommentAndUser.time"></time>
</div>
<button type="button" class="layui-btn reply-btn" v-on:click="replyComment(CommentAndUser.id)">回复
</button>
</li>
<div v-if="CommentAndUser.childList" class="children-item">
<list :list="CommentAndUser.childList"></list>
</div>
</ul>
</div> `,
});
new Vue({
el: "#app",
data: {
commentAndUserList: []
},
created:
function () {
//这里ajax或者axios请求更新commentAndUserList即可
}
})
</script>
然后大功告成,最后的结果长这样:
更多推荐
所有评论(0)