专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员的那些事  ·  北京大学出的第二份 DeepSeek ... ·  13 小时前  
程序员的那些事  ·  印度把 DeepSeek ... ·  3 天前  
OSC开源社区  ·  2024: 大模型背景下知识图谱的理性回归 ·  4 天前  
程序员小灰  ·  DeepSeek做AI代写,彻底爆了! ·  5 天前  
51好读  ›  专栏  ›  SegmentFault思否

如何实现一个楼中楼的评论系统?

SegmentFault思否  · 公众号  · 程序员  · 2017-12-20 08:00

正文

1. 实现前的思考

在经历过多说和网易云跟帖后,总算是下定决心自己要写一个评论系统了。

我们在使用的很多评论系统中,目前比较流行的就是楼中楼的方式了,比如百度贴吧,wordpress等等。在这以前,一般都是按照时间顺序进行1楼、2楼、3楼的展示,如果要回复某个人,使用 @ 符号标识出这个用户的名字,然后回复内容。可是这样存在一个很大的问题,讨论问题没有集中在一起,其他用户根本不知道你们在讨论什么,原作者在1楼发表评论,你进来回复这个用户的评论时,已经到10楼了,原作者再回复你又到20楼了。其他用户看到10楼时,早已经忘记原作者说了什么了。

百度贴吧在改版之前就是这种方式,后来在新版中启用了楼中楼的方式,这种方式,关于某个话题的讨论就能集中在一块了。

同时,知乎也对他的评论系统进行了一次改版,不过不是改版成楼中楼,而是在每个有对话评论的后面加上一个弹窗链接 查看对话 ,点击链接后弹窗能看到这两个人之间互动的所有评论。

采用时间顺序倒序或者正序平铺的方式展示评论,这种方式实现起来简单,但是阅读困难;采用楼中楼的方式展示评论,对用户的阅读习惯比较友好,但是实现起来可能比较困难。不过,最后仍然决定采用 楼中楼 的方式来,虽然本人博客的评论量也少的可怜,不过还是决定要实现一下。


2. 数据表的设计

先说下前后端使用的语言和框架,前端考虑到页面渲染和比较多的事件调用,使用了vue框架,vue应该说不是最好的选择,毕竟对一个评论的前端部门来说,可能有点大材小用,不过为了快速开发,也就选择了vue。后端使用的是php语言,数据库使用的是mysql。

数据库表的设计,既要考虑到可以导入以前的数据,又能方便以后添加新的评论。这里我创建了3个表: 文章表,用户表,评论表。

在网易云跟帖关闭之前,我把自己的数据导出来了(多说的数据已经丢失,不知道导出的格式是什么了),我们来看下网易云跟帖里导出数据的格式:

  1. {

  2.    "title": "从0到1学习node(七)之express搭建简易论坛",

  3.    "url": "www.xiabingbao.com/node/2017/02/20/node-express-forum.html",

  4.    "sourceId": "",

  5.    "ctime": 1487581007000,

  6.    "comments": [

  7.        {

  8.            "cid": "72813956",

  9.            "ctime": 1493107384000,

  10.             "content": "这个论坛对node 的版本有要求吗  我的node比较老 下载你的源码   访问出错",

  11.            "pid": "0",

  12.            "ip": "xxx",

  13.            "port": 0,

  14.            "sc": "web",

  15.            "vote": 0,

  16.            "against": 0,

  17.            "anonymous": false,

  18.            "user": {

  19.                "userId": "1074123",

  20.                "nickname": "有态度网友06q23q",

  21.                "avatar": "",

  22.                "anonymous" : false

  23.            }

  24.        },

  25.        {

  26.            "cid": "77196403",

  27.            "ctime": 1493714822000,

  28.            "content": "不太清楚,我这里node版本是6.9.4,npm版本是3.10.10,你试一下升级node版本",

  29.            "pid": "72813956",

  30.            "ip": "xxx",

  31.            "port": 0,

  32.            "sc": "web",

  33.            "vote": 0,

  34.            "against": 0,

  35.            "anonymous": false,

  36.            "user": {

  37.                "userId": "2414123",

  38.                "nickname": "小小dd蚊子",

  39.                "avatar": "http://cms-bucket.nosdn.127.net/1d6faddedb544cee93ff426a4aa2fe7620170322162349.jpg",

  40.                "anonymous": false

  41.            }

  42.        }

  43.    ]

  44. }

从上面的数据里,可以看到,每个文章都有标题,url和评论,评论的每一项都有自己对应的id,其回复的评论pid,内容content,该评论的用户userid, nickname和avatar。我这里也就只摘取主要的信息录入到数据库中。

2.1 用户表

用户表相对来说比较简单,要考虑的就是原有的userid也要作为字段进行保存,方便在导入评论数据时能找到对应的用户,在评论数据也导入完成后即可将该字段删除,以后新添加的注册用户用不到这个字段了。用户表的设计:

字段 类型 说明
id int 自增,主键
wid int 用户原有的userid
nickname varchar(50) 昵称
avatar varchar(100) 头像
status int 状态

设计好用户表后,将原数据中所有的用户都单独拿出来,然后使用userid作为key存储到一个数组中,这样也能起到一个去重的效果。把拿到的所有的用户数据存储到用户表

2.2 评论表

在设计评论表,主要考虑如下的因素:

  • 评论必须依托于文章和用户才能存在,因此评论的外键是文章标识和userid,留言板是一个文章内容为空的评论形式;

  • 我想以后新的评论能使用自增id,而不是跟随原有评论的cid来产生新的评论id,因此这次评论表的主键是id,原有的评论id只作为其中的一个字段wid来构造楼中楼的关系,这些旧评论插入到数据表时都会有新的评论id;

  • 楼中楼的评论是处在某个评论下的,同时,楼中楼里还有相互之前的互动回复。因此这个评论的pid(parentid)表示当前评论处于哪个评论之下,同时replyid表示是回复的哪个评论;若直接回复的父级评论,则pid与replyid相同,都是父级评论的id,若回复的不是父级评论,则pid为父级评论的id,replyid为回复评论的id;pid或replyid为0时,则表示直接对文章发表评论。

因此我们的评论表是这样设计的:

字段 类型 说明
id int 自增,主键
wid int 评论原有的主键cid
uid int 用户id
replyid int 该评论回复的评论id,没有则为0
pid int 该评论所在的父级id,没有则为0
aid varchar(100) 文章的标识
content varchar(300) 评论内容
createtime int 评论时间的时间戳

表中的aid(文章的标识)可以是文章的url,文章的id或者其他任何能够唯一识别该文章的东东都可以。这里我们使用的是文章的uri来作为唯一标识,比如上面数据中的文章,我们使用 /node/ 2017 / 02 / 20 / node - express - forum . html 来标识文章。其他文章同理。

将这些评论写入表时,我们还要注意的是,原数据中,每个评论都对应着一个用户,在我设计的系统里,用户与评论分来了,只使用uid来进行关联。新的用户与新的评论都是使用自有的自增主键,因此在原有评论进行入库时,需要将原来的userid转换为新用户表中的主键id,新旧数据进行统一。

文章表不做解释。


3. 具体实现

前端部分主要是负责展示每个文章的评论,同时让登录用户可以添加评论。

3.1 展示评论

我们已经对每个评论都添加了文章标识,前端只要根据aid就能拿到当前文章所有的评论。不过我们的评论是要 楼中楼 的方式展示的,不能一股脑的把数据平铺到页面中。我们在2.2中也说了,pid为0的评论都是直接对文章进行评论的,这些评论应该是作为一级评论展示的;pid为其他数据的,必然是属于某个评论之下,应当作为楼中楼展示。

同时,无论一级评论,还是楼中楼的评论,都有可能产生分页的情况,因此这里也要做好分页处理。

那么最终,我们前端拿到的结构应该大致是这样的:

  1. {

  2.    code : 0,

  3.    data : [

  4.        {// 一级评论

  5.            id : 1,

  6.            content : '蚊子的博客棒棒的',

  7.            createtime : '2017年08月08日00:26',

  8.            nickname : '匿名者',

  9.            reply : {// 楼中楼

  10.                data : [

  11.                    id :







请到「今天看啥」查看全文