web页面 之 评论盖楼
一、前言
评论是人与人之间,在网络上交互的一个重中之重的窗口,这在前端当中也算是一个不可或缺的模块!
我们浏览各种能吐槽,能刷人气的网站,在各种头条的下面,都有评论这一项!我们围观水军吐槽的时候会发现,他们的评论楼有的是特别有层次感,一眼忘穿秋水知道怎么个评论大法,而有的设计的是一楼一楼的盖起来,要评论别人也是盖一层!简直是没有耐心去观战!估计是他们的前端想草草了事,整了个这么友好性近乎为零的评论楼,看的时候作为看官的我,想咂电脑的心都有。所以今天我们就简单的聊聊层次分明的盖楼方法!
二、原理
#我们前提规定评论的内容都存在数据库里,字段与下面字典中的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),