实现网站的评论功能

1,107 阅读5分钟

效果

先看效果。

初始状态下的评论,全部展示回复数不足2条的数据:

回复数大于5条,后台只默认返回回复前2条数据:

下拉滚动条,触底自动加载下一页评论数据:

评论分页

目前大部分的评论,前端展示都不再是多级嵌套结构的了,只有某些老式的网站还保留前端多级嵌套的评论结构,这种评论结构随着嵌套层级过深,会给用户一种排列及其凌乱的视觉效果。

而且最大的问题在于,无法对评论数据进行分页。

尽管可以通过sql递归查询来获取pid为0的数据及其子数据,对pid为0的数据进行分页,取得id并递归获取其所有子回复的数据,返回给前端的数据结构为:

效果如下:

但这种分页没有任何意义。

所以如今的评论结构大致来说就两层:评论层+回复层,这种设计方式,能够更好地解决评论分页问题,比如掘金:

点开评论,查看评论下的所有回复:

能明显看出来,评论数据和回复数据并非同一库表的数据,至少使用了comment评论表和replay回复表。

对评论进行分页,查询评论下的总的回复数,当回复数大于2条时,再通过评论的id去拿回复的全部列表,评论功能大致就这样。

提交评论

对于前端来说其实还有个问题,就是如何去更新列表数据的状态,比如新增评论或删除评论,以前我是在操作成功后,动态请求当前页面数据,不过这回造成一个问题,页面会闪动,而且特别是流加载的数据,重新请求会导致一些其他的问题,比如分页参数不一致导致页面数据叠加冗余。

最好的办法就是前端静态构造,只需要得知后台接口请求成功了,前端在当前里列表数据中静态添加一条和后台数据契合的数据。

需要做得更细致些的化,可以在新增的这条数据前加个转圈的状态,接口请求成功后这个状态就正式更新,快手就是这样做的。

查询评论

还有个功能是查询评论,即A用户给B用户回复消息,B用户可以通过消息提醒,快速跳转到A用户的回复详情下。

这个功能相对来说也时分简单,前端弹出列表,该列表展示A用户的评论详情以及下辖的子回复数据,然后给当前回复的评论添加一个颜色变化的效果,表示是当前评论触发的消息。

很多大型网站的思路都是这样的,比如快手、哔哩哔哩和网易云,他们的评论都是做了分页,直接跳到评论主体下去进行高亮展示显然不现实,他们的做法就是在当前评论页面新开一个弹框,显示评论详情。 

具体的编码业务如下:消息通知携带回复的id,回复id携带评论id,评论id又携带主体id,那么久可以连带查出从主体到评论,从评论到回复的链式数据:

  async getReplay(params){

let sql = `

WITH RECURSIVE folder_recursion AS (

SELECT id, pid, content, createtime,userid,

IF(COMMENT.userid=:userid,1,0) isyour,

(SELECT count(*) issupport FROM support WHERE COMMENT.id = support.targetid AND userid = :userid AND type = 101) issupport,

(SELECT count(*) isfavorite FROM favorite WHERE COMMENT.id = favorite.targetid AND userid = :userid AND type = 101) isfavorite,

(SELECT COUNT(*) FROM support WHERE type = 101 AND targetid = COMMENT.id) supportcount,

(SELECT COUNT(*) FROM favorite WHERE type = 101 AND targetid = COMMENT.id) favoritecount,

(SELECT username from user WHERE COMMENT.userid = user.id) username,

(SELECT headimg from user WHERE COMMENT.userid = user.id) headimg

FROM COMMENT

WHERE pid = :id

UNION ALL

SELECT c.id, c.pid, c.content,c.createtime, c.userid,

IF(c.userid=:userid,1,0) isyour,

(SELECT count(*) issupport FROM support WHERE c.id = support.targetid AND userid = :userid AND type = 101) issupport,

(SELECT count(*) isfavorite FROM favorite WHERE c.id = favorite.targetid AND userid = :userid AND type = 101) isfavorite,

(SELECT COUNT(*) FROM support WHERE type = 101 AND targetid = c.id) supportcount,

(SELECT COUNT(*) FROM favorite WHERE type = 101 AND targetid = c.id) favoritecount,

(SELECT username from user WHERE c.userid = user.id) username,

(SELECT headimg from user WHERE c.userid = user.id) headimg

FROM COMMENT c

INNER JOIN folder_recursion fr ON c.pid = fr.id

) SELECT * FROM folder_recursion ORDER BY createtime desc;

`

let [results_son] = await Model.sequelize.query(sql,{

replacements:{

userid:params.userid??null,

id:params.id

}

})

return results_son

}

前端的实现方式,通过在路由上增加一个commentid和page=message参数,表示当前页面是从消息中心跳转过来的,并拿到当前的commentid,然后获取回复数据,就直接在消息页面做弹框展示。

 大概这样:

但这个消息详情放置在文章详情不太合理,应该在消息页面触发,并且给出实体内容。

那么评论就暂时到此为止,下节做消息通知,顺便把评论详情这块儿内容完成。