一、前言
评论是人与人之间,在网络上交互的一个重中之重的窗口,这在前端当中也算是一个不可或缺的模块!
我们浏览各种能吐槽,能刷人气的网站,在各种头条的下面,都有评论这一项!我们围观水军吐槽的时候会发现,他们的评论楼有的是特别有层次感,一眼忘穿秋水知道怎么个评论大法,而有的设计的是一楼一楼的盖起来,要评论别人也是盖一层!简直是没有耐心去观战!估计是他们的前端想草草了事,整了个这么友好性近乎为零的评论楼,看的时候作为看官的我,想咂电脑的心都有。所以今天我们就简单的聊聊层次分明的盖楼方法!
二、原理
#我们前提规定评论的内容都存在数据库里,字段与下面字典中的key一致!当我们从数据库中取出数据,然后实现评论楼!
#从数据库中以字典类型读取到数据,这些数据存放在一个列表里,对这个列表进行循环,先为每个字典加入一对键值对"'child':[]",判断每个字典中的父ID是否有值,
#没值证明是鼻祖,有值就找到对应的父ID,把自己加入到其孩子键为"child"所对应的列表里,依次类推对所有的数据进行一次整理!
#数据库获取到的数据
msg_list = [
{'id':1,'content':'xxx','parent_id':None},
{'id':2,'content':'xxx','parent_id':None},
{'id':3,'content':'xxx','parent_id':None},
{'id':4,'content':'xxx','parent_id':1},
{'id':5,'content':'xxx','parent_id':4},
{'id':6,'content':'xxx','parent_id':2},
{'id':7,'content':'xxx','parent_id':5},
{'id':8,'content':'xxx','parent_id':3},
]
"""
数据重新整合之后,按照我们的逻辑得到的数据
msg_list = [
{
'id':1,'content':'xxx',parent_id:None,child:[
{'id':4,'content':'xxx',parent_id:1},
{'id':5,'content':'xxx',parent_id:1,child:[
{'id':7,'content':'xxx',parent_id:5},
]}]
},
{'id':2,'content':'xxx',parent_id:None,child:[
{'id':6,'content':'xxx',parent_id:2},
]},
{'id':3,'content':'xxx',parent_id:None,child:[
{'id':8,'content':'xxx',parent_id:3},
]},
]
"""
"""
最终效果展示:
评论1
评论4
评论6
评论5
评论2
评论3
"""
针对这种显示方式,我们有两种方法:
一种是在服务端这边,通过函数做好数据的整合然后处理成要显示的效果的HTML标签字符串,然后发送给客户端;
另一种是把整合好的数据发送给客户端,让客户端去实现最终显示的效果!
由于有子父ID判断的问题,需要用到递归函数,递归函数的弊端就是执行慢,严重占用机器的性能!如果是多人次的访问就会给服务器带来很大的压力;
而把数据传递到客户哪里,让客户的页面通过JS实现最终显示,虽然是把递归放到了客户端上,但是从全局上讲客户端仅仅是处理这个递归函数,而服务端仅是一个数据整合的功能,会大大提交服务器的效率!
#按照这个重新整合出来的已经分好等级的数据,通过父ID和child进行整理拼接,在页面上实现有层级的评论楼!
#python里面的apend之类的东西都是引用的原来数据的内从地址,对原数据进行操作的话
#我们引用的数据也会发生一样的变化(字典列表之类的)
#浙江吴彦祖的方法:
# for i in msg_list:
# i['child']=[]
# for i in range(len(msg_list)-1,-1,-1):
# if msg_list[i]['parent_id']:
# msg_list[msg_list[i]['parent_id'] - 1]['child'].append(msg_list[i])
# new_msg_list = [i for i in msg_list if i['parent_id'] is None]
# print(new_msg_list)
#老师讲的方法
# v=[row.setdefault('child',[]) for row in msg_list] #这和地下的第一个for循环的作用是一样的,给每一个元素加一个'child':[]
# print(msg_list)
#如果我们想加快索引(快点找到数据的话)就建一个字典的数据结构,这是因为字典中的key会以一种特殊方式存于内存之中,他这种结构就是为了方便查找对应的值!
msg_list_dict={} #加快索引,节省时间
for item in msg_list:
item['child']=[]
msg_list_dict[item['id']]=item #字典中key为item['id'],value为item
#把字典数据结构填上数据,能够加快索引,而且我们数据还是占得原来的内从空间
#我们只是引用了数据的内容空间,所以不存在新的数据结构浪费空间一说
result=[]
for item in msg_list:
pid=item['parent_id']
if pid: #如果parent_id不为空,说明它是子级,要把自己加入对应的父级
msg_list_dict[pid]['child'].append(item)
else: #如果为空,说明他是父级,鼻祖级别,要把它单独立出来用
result.append(item)
#result就是我们最终要的结果,因为这里面全是引用,所有数据的内存地址都没有变
#只不过被多个数据结构引用了而已
print(result)
三、精简版本
Django app中---->views.py函数:
def comments(request,nid):
"""
评论
:param request:
:param nid:
:return:
"""
ret = {"status":True,"data":None,"msg":None}#定义初始值
try:
msg_list = models.Comment.objects.values("nid","article_id","content","create_time","reply_id","user_id")
msg_list_dict = {} # 定义一个字典
for item in msg_list:
item["child"] = [] # 给列表中的每个字典添加一对键值对 "child"=[]
msg_list_dict[item["nid"]] = item # 把数据重组成一个新的字典,以id为key,原字典为values
result = [] # 定义一个新列表
for item in msg_list:
pid = item["reply_id"] # 父id
if pid: # 如果父id存在的话
msg_list_dict[pid]["child"].append(item) # 就从新字典中为id为父id的child添加值
else:
result.append(item) # 否则的话,就把对应的字典添加到新列表中(列表信息重组,把重复的值去掉)。
ret["data"] = result
except Exception as e:
ret["status"] = False
ret["msg"] = str(e)
return HttpResponse(json.dumps(ret))
Django 中模版 HTML代码:
//我就是尝试着加了个框,和能看到位置手写的值
<div class="comments">
<div></div>
<div class="coms_head">评论列表</div>
<div class="coms_body">
<div class="coms_items_title">#1楼</div>
<div></div>
<div class="coms_items_info">你猜我写的啥?</div>
<div id="commentArea"></div>
</div>
</div>
重点来啦!JS实现多级评论:
<script type="text/javascript" src="/static/js/jquery-3.2.1.js"></script>
<script>
$(function(){
$.ajax({
url:"/comments-{{ articles.nid }}.html",
type:"GET", //以GET方式获取数据
dataType:"JSON",
success:function(arg){
if(arg.status){
var comment = commentTree(arg.data);
$("#commentArea").append(comment);
} else{
alert(arg.msg);
}
}
})
});
//自定义字符串格式化的方法(有一张博客里写过!)
String.prototype.Format = function(arg){
var temp = this.replace(/\{(\w+)\}/g,function(k,kk){
return arg[kk];
});
return temp;
};
//简单实现评论楼的功能,通过递归判断信息中有没有child项,以实现主评论和子评论之间有层次关系
function commentTree(commentList){
var comment_str = "<div class='comment' >";
$.each(commentList,function(k,row){
var temps = "<div class='content'>{content}</div>".Format({"content":row.content});
comment_str += temps;
if (row.child.length>0){
comment_str +=commentTree(row.child);
}
});
comment_str += "</div>";
return comment_str;
}
</script>
必不可少的路由:
url(r'^comments-(\w+).html$',views.comments),