文章内容由蚊子的博客进行发布,或许你想看看他其余的博客呢: https://www.xiabingbao.com/comments/2017/09/01/blog-comments.htmljavascript
在经历过多说和网易云跟帖后,总算是下定决心本身要写一个评论系统了。php
咱们在使用的不少评论系统中,目前比较流行的就是楼中楼的方式了,好比百度贴吧,wordpress等等。在这之前,通常都是按照时间顺序进行1楼、2楼、3楼的展现,若是要回复某我的,使用@
符号标识出这个用户的名字,而后回复内容。但是这样存在一个很大的问题,讨论问题没有集中在一块儿,其余用户根本不知道大家在讨论什么,原做者在1楼发表评论,你进来回复这个用户的评论时,已经到10楼了,原做者再回复你又到20楼了。其余用户看到10楼时,早已经忘记原做者说了什么了。html
百度贴吧在改版以前就是这种方式,后来在新版中启用了楼中楼的方式,这种方式,关于某个话题的讨论就能集中在一块了。前端
同时,知乎也对他的评论系统进行了一次改版,不过不是改版成楼中楼,而是在每一个有对话评论的后面加上一个弹窗连接查看对话
,点击连接后弹窗能看到这两我的之间互动的全部评论。vue
采用时间顺序倒序或者正序平铺的方式展现评论,这种方式实现起来简单,可是阅读困难;采用楼中楼的方式展现评论,对用户的阅读习惯比较友好,可是实现起来可能比较困难。不过,最后仍然决定采用楼中楼
的方式来,虽然本人博客的评论量也少的可怜,不过仍是决定要实现一下。java
先说下先后端使用的语言和框架,前端考虑到页面渲染和比较多的事件调用,使用了vue框架,vue应该说不是最好的选择,毕竟对一个评论的前端部门来讲,可能有点大材小用,不过为了快速开发,也就选择了vue。后端使用的是php语言,数据库使用的是mysql。node
数据库表的设计,既要考虑到能够导入之前的数据,又能方便之后添加新的评论。这里我建立了3个表: 文章表,用户表,评论表。mysql
在网易云跟帖关闭以前,我把本身的数据导出来了(多说的数据已经丢失,不知道导出的格式是什么了),咱们来看下网易云跟帖里导出数据的格式:git
{ "title": "从0到1学习node(七)之express搭建简易论坛", "url": "www.xiabingbao.com/node/2017/02/20/node-express-forum.html", "sourceId": "", "ctime": 1487581007000, "comments": [ { "cid": "72813956", "ctime": 1493107384000, "content": "这个论坛对node 的版本有要求吗 个人node比较老 下载你的源码 访问出错", "pid": "0", "ip": "xxx", "port": 0, "sc": "web", "vote": 0, "against": 0, "anonymous": false, "user": { "userId": "1074123", "nickname": "有态度网友06q23q", "avatar": "", "anonymous": false } }, { "cid": "77196403", "ctime": 1493714822000, "content": "不太清楚,我这里node版本是6.9.4,npm版本是3.10.10,你试一下升级node版本", "pid": "72813956", "ip": "xxx", "port": 0, "sc": "web", "vote": 0, "against": 0, "anonymous": false, "user": { "userId": "2414123", "nickname": "小小dd蚊子", "avatar": "http://cms-bucket.nosdn.127.net/1d6faddedb544cee93ff426a4aa2fe7620170322162349.jpg", "anonymous": false } } ] }
从上面的数据里,能够看到,每一个文章都有标题,url和评论,评论的每一项都有本身对应的id,其回复的评论pid,内容content,该评论的用户userid, nickname和avatar。我这里也就只摘取主要的信息录入到数据库中。github
用户表相对来讲比较简单,要考虑的就是原有的userid也要做为字段进行保存,方便在导入评论数据时能找到对应的用户,在评论数据也导入完成后便可将该字段删除,之后新添加的注册用户用不到这个字段了。用户表的设计:
字段 | 类型 | 说明 |
---|---|---|
id | int | 自增,主键 |
wid | int | 用户原有的userid |
nickname | varchar(50) | 昵称 |
avatar | varchar(100) | 头像 |
status | int | 状态 |
设计好用户表后,将原数据中全部的用户都单独拿出来,而后使用userid做为key存储到一个数组中,这样也能起到一个去重的效果。把拿到的全部的用户数据存储到用户表
在设计评论表,主要考虑以下的因素:
评论必须依托于文章和用户才能存在,所以评论的外键是文章标识和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,新旧数据进行统一。
文章表不作解释。
前端部分主要是负责展现每一个文章的评论,同时让登陆用户能够添加评论。
咱们已经对每一个评论都添加了文章标识,前端只要根据aid就能拿到当前文章全部的评论。不过咱们的评论是要楼中楼
的方式展现的,不能一股脑的把数据平铺到页面中。咱们在2.2中也说了,pid为0的评论都是直接对文章进行评论的,这些评论应该是做为一级评论展现的;pid为其余数据的,必然是属于某个评论之下,应看成为楼中楼展现。
同时,不管一级评论,仍是楼中楼的评论,都有可能产生分页的状况,所以这里也要作好分页处理。
那么最终,咱们前端拿到的结构应该大体是这样的:
{ code : 0, data : [ {// 一级评论 id : 1, content : '蚊子的博客棒棒的', createtime : '2017年08月08日00:26', nickname : '匿名者', reply : {// 楼中楼 data : [ id : 1, content : '谢谢,一块儿学习', createtime : '2017年08月08日00:36', nickname : '蚊子' ], page : { cur_page : 1, all_page : 1 } } } ], page : { cur_page : 1, all_page : 1 } }
前端拿到接口返回的数据后,就能够渲染页面了。在头像的处理上,也考虑到了https的环境,所以返回的头像连接都是//
开头的形式。
用户对文章或者某个评论产生了共鸣,须要留言讨论一番,咱们就须要用户可以把本身的评论也添加进去。
评论的类型,细分的话,能够分为3类:
直接对文章发表评论,pid与replyid为空;
对一级评论进行回复,pid与replyid均为一级评论的id;
对楼中楼进行回复,pid为一级评论的id,replyid为你回复的评论的id
我这里前端的实现参考了oschina(开源中国)的评论方式。直接对文章评论,是直接在顶部的评论窗口进行输入;对其余评论进行回复时,采用弹窗的方式来进行回复。弹窗回复的好处就是,页面不用滚动,用户对某个评论的感知也能停留在这个位置;同时也不用增长各类没必要要的小输入框来让用户输入评论。
在登陆问题上,我也是纠结了很多的时间,到底是使用本身的登陆系统呢,仍是使用第三方登陆呢,或者是用户不用注册登陆,只要输入邮箱和昵称就能进行评论呢?
使用本身的评论系统,那么就须要开发一套注册和登陆流程,开发麻烦,并且对于想要回复一句话的用户来讲,可能就直接放弃注册了;若只要输入邮箱和昵称就能评论,我考虑到可能会引发用户的无限评论,没法控制。所以,最后仍是考虑接入第三方的登陆,这里选择了使用微博做为第三方登陆的入口,后续会考虑加入github的账号登陆。
关于如何接入微博的第三方登陆,咱们下篇文章再讲,文档齐全,对不熟悉的开发者来讲,刚开始可能有点懵逼,不过应该问题不大。
用户在第三方登陆成功后,在名字旁边有个小的input输入框,可让用户输入邮箱来接收回复提醒,这个输入彻底是自愿的,不输入邮箱也依然能够评论。也是考虑到本站是个小站,访问量极低,用户可能一时兴起评论了两句,过后又想起这个网站来,又不知道怎么找了。所以就想着添加一个邮件提醒功能,不让大神的评论石沉大海。
前端部门引入了vue框架,评论模块在每一个文章页都会加载。为了防止评论模块中的vue库对外部的资源形成影响(好比版本冲突等),我先把全局变量给了wzVue,而后在把Vue注销掉:
wzVue = Vue; delete(Vue); delete(window.Vue);
同时,在刚开始实现完成评论功能的时候,用户只要进到这个页面,评论就会加载。可是有个问题就是,用户不必定会把你的文章看到底部,不必定就看你的评论。所以后来文章就改为了按需加载,只有用户滚动到底部,有想要看评论的意向时采起加载评论。
最终展现的效果就是这样:
做为一名前端开发,用仅有的后端知识开发一套博客的评论系统,显得是很是的简陋,整个框架的设计感受也是很糙。同时缓存系统用的不熟练,不能作到评论信息的当即更新。这个系统依然还有不少改进的地方。欢迎你们对蚊子(师少兵)多多提意见和建议。
在写这篇文章的时候,想着是之后要改版的时候,能够作成评论同步加载的方式进行。生成后的文章,更新频率极低,甚至不太变更,那么缓存的就是评论的内容,每当有新的评论时,就删除当前文章的缓存,从新加载新的数据,而后再缓存上新的数据,这样在评论数据更新比较低的时候,能够缓存的时间更长,同时也有利于搜索引发对评论内容的抓取。
文章内容由蚊子的博客进行发布,或许你想看看他其余的博客呢: https://www.xiabingbao.com/comments/2017/09/01/blog-comments.html