欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何利用Vue+SpringBoot實(shí)現(xiàn)評(píng)論功能

 更新時(shí)間:2023年06月05日 08:37:34   作者:瑤琴遇知音  
簡單的評(píng)論功能是指能夠在文章底下進(jìn)行評(píng)論,而且能夠?qū)υu(píng)論進(jìn)行回復(fù),下面這篇文章主要給大家介紹了關(guān)于如何利用Vue+SpringBoot實(shí)現(xiàn)評(píng)論功能的相關(guān)資料,需要的朋友可以參考下

前言

評(píng)論系統(tǒng)相信大家并不陌生,在社交網(wǎng)絡(luò)相關(guān)的軟件中是一種常見的功能。然而對于初學(xué)者來說,實(shí)現(xiàn)一個(gè)完整的評(píng)論系統(tǒng)并不容易。本文筆者以 Vue+SpringBoot 前后端分離的架構(gòu)細(xì)說博客評(píng)論功能的實(shí)現(xiàn)思路。

難點(diǎn)

對于一個(gè)評(píng)論系統(tǒng)主要包含評(píng)論人,評(píng)論時(shí)間,評(píng)論內(nèi)容,評(píng)論回復(fù)等內(nèi)容。此外可能還存在回復(fù)的回復(fù)以及回復(fù)的回復(fù)的回復(fù),每條評(píng)論可能存在多條回復(fù),每條回復(fù)又可能存在多條回復(fù),即是一個(gè)多叉樹的關(guān)系。因此,難點(diǎn)如下:

  • 確定并存儲(chǔ)評(píng)論與回復(fù)的層級(jí)關(guān)系以及與博客本章的從屬關(guān)系
  • 多層級(jí)評(píng)論與回復(fù)的前端遞歸顯示
  • 多層級(jí)評(píng)論與回復(fù)的遞歸刪除

實(shí)現(xiàn)思路

數(shù)據(jù)表設(shè)計(jì)

首先我們需要考慮的是數(shù)據(jù)表中如何存儲(chǔ)評(píng)論與回復(fù)的層級(jí)關(guān)系以及與博客文章的從屬關(guān)系。

  • 很直觀能夠想到對于每一條評(píng)論,擁有一個(gè)表示所屬博客文章ID的字段blogId
  • 每一條評(píng)論維護(hù)一個(gè)parentId字段,表示父評(píng)論的id,由此確定評(píng)論之間的層級(jí)關(guān)系
  • 此外我們還會(huì)維護(hù)一個(gè)rootParentId字段,表示當(dāng)前評(píng)論所屬根評(píng)論的id,該字段將在前端遞歸顯示時(shí)有大用

于是,添加上其他相關(guān)信息后最終的數(shù)據(jù)表schema如下:

字段名稱中文注釋數(shù)據(jù)類型是否為null備注
id評(píng)論idbigintnot nullprimary key,auto increment
content評(píng)論內(nèi)容textnot null
user_id評(píng)論人idbigintnot null
user_name評(píng)論人姓名varchar(80)
create_time創(chuàng)建時(shí)間datetime
is_delete是否已刪除tinyintdefault 00:未刪除;1:已刪除
blog_id所屬博客idbigint
parent_id父評(píng)論idbigint
root_parent_id根評(píng)論idbigint

數(shù)據(jù)傳輸格式設(shè)計(jì)

基于數(shù)據(jù)表schema,我們需要設(shè)計(jì)前后端數(shù)據(jù)傳輸?shù)母袷?,以方便前后端對于層?jí)關(guān)系的解析。

  • 很自然地想到將評(píng)論的基本信息封裝為 bean,并將其子評(píng)論對象封裝為其一個(gè)屬性。
  • 由于每條評(píng)論可能存在多條回復(fù),因此屬性的數(shù)據(jù)類型應(yīng)當(dāng)為 List

于是得到的評(píng)論 bean 為:

/**
 * 評(píng)論信息
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Comment implements Serializable {

    private Long id;       // 評(píng)論ID
    private String content;       // 評(píng)論內(nèi)容
    private Long userId;          // 評(píng)論作者ID
    private String userName;      // 評(píng)論作者姓名
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;      // 創(chuàng)建時(shí)間
    private Integer isDelete;     // 是否刪除(0:未刪除;1:已刪除)

    private Long blogId;      // 博客ID
    private Long parentId;    // 父評(píng)論ID(被回復(fù)的評(píng)論)
    private Long rootParentId;      // 根評(píng)論ID(最頂級(jí)的評(píng)論)

    private List<Comment> child;    // 本評(píng)論下的子評(píng)論
}

那么接下來的問題是如何將數(shù)據(jù)表中的層級(jí)關(guān)系轉(zhuǎn)化為 Comment 類中的 father-child 的關(guān)系

我這里寫了一個(gè) util 的方法完成這個(gè)轉(zhuǎn)化過程

/**
* 構(gòu)建評(píng)論樹
* @param list
* @return
*/
public static List<Comment> processComments(List<Comment> list) {
    Map<Long, Comment> map = new HashMap<>();   // (id, Comment)
    List<Comment> result = new ArrayList<>();
    // 將所有根評(píng)論加入 map
    for(Comment comment : list) {
        if(comment.getParentId() == null)
            result.add(comment);
        map.put(comment.getId(), comment);
    }
    // 子評(píng)論加入到父評(píng)論的 child 中
    for(Comment comment : list) {
        Long id = comment.getParentId();
        if(id != null) {   // 當(dāng)前評(píng)論為子評(píng)論
            Comment p = map.get(id);
            if(p.getChild() == null)    // child 為空,則創(chuàng)建
                p.setChild(new ArrayList<>());
            p.getChild().add(comment);
        }
    }
    return result;
}

這樣父子關(guān)系就表示清楚了,前端通過接口請求到的數(shù)據(jù)就會(huì)是如下的樣子

{
    "success": true,
    "code": 200,
    "message": "執(zhí)行成功",
    "data": {
        "commentList": [
            {
                "id": 13,
                "content": "r34r43r4r54t54t54",
                "userId": 1,
                "userName": "admin",
                "createTime": "2022-10-26 04:53:21",
                "isDelete": null,
                "blogId": 1,
                "parentId": null,
                "rootParentId": null,
                "child": [
                    {
                        "id": 19,
                        "content": "評(píng)論回復(fù)測試2",
                        "userId": 1,
                        "userName": "admin",
                        "createTime": "2022-10-27 03:10:41",
                        "isDelete": null,
                        "blogId": 1,
                        "parentId": 13,
                        "rootParentId": 13,
                        "child": null
                    }
                ]
            },
            {
                "id": 12,
                "content": "fdfgdfgfg",
                "userId": 1,
                "userName": "admin",
                "createTime": "2022-10-26 04:51:46",
                "isDelete": null,
                "blogId": 1,
                "parentId": null,
                "rootParentId": null,
                "child": [
                    {
                        "id": 20,
                        "content": "評(píng)論回復(fù)測試3",
                        "userId": 1,
                        "userName": "admin",
                        "createTime": "2022-10-27 03:16:09",
                        "isDelete": null,
                        "blogId": 1,
                        "parentId": 12,
                        "rootParentId": 12,
                        "child": null
                    }
                ]
            },
            {
                "id": 11,
                "content": "demo",
                "userId": 1,
                "userName": "admin",
                "createTime": "2022-10-26 04:12:43",
                "isDelete": null,
                "blogId": 1,
                "parentId": null,
                "rootParentId": null,
                "child": [
                    {
                        "id": 21,
                        "content": "評(píng)論回復(fù)測試4",
                        "userId": 1,
                        "userName": "admin",
                        "createTime": "2022-10-27 03:19:42",
                        "isDelete": null,
                        "blogId": 1,
                        "parentId": 11,
                        "rootParentId": 11,
                        "child": null
                    }
                ]
            },
            {
                "id": 9,
                "content": "評(píng)論3",
                "userId": 3,
                "userName": "zhangsan",
                "createTime": "2022-10-05 06:20:54",
                "isDelete": null,
                "blogId": 1,
                "parentId": null,
                "rootParentId": null,
                "child": [
                    {
                        "id": 24,
                        "content": "評(píng)論回復(fù)測試n3",
                        "userId": 1,
                        "userName": "admin",
                        "createTime": "2022-10-27 03:23:54",
                        "isDelete": null,
                        "blogId": 1,
                        "parentId": 9,
                        "rootParentId": 9,
                        "child": null
                    }
                ]
            },
            {
                "id": 7,
                "content": "評(píng)論2",
                "userId": 2,
                "userName": "liming",
                "createTime": "2022-10-05 06:19:40",
                "isDelete": null,
                "blogId": 1,
                "parentId": null,
                "rootParentId": null,
                "child": [
                    {
                        "id": 8,
                        "content": "回復(fù)2-1",
                        "userId": 1,
                        "userName": "admin",
                        "createTime": "2022-10-14 06:20:07",
                        "isDelete": null,
                        "blogId": 1,
                        "parentId": 7,
                        "rootParentId": 7,
                        "child": null
                    }
                ]
            },
            {
                "id": 1,
                "content": "評(píng)論1",
                "userId": 1,
                "userName": "admin",
                "createTime": "2022-10-05 06:14:32",
                "isDelete": null,
                "blogId": 1,
                "parentId": null,
                "rootParentId": null,
                "child": [
                    {
                        "id": 3,
                        "content": "回復(fù)1-2",
                        "userId": 2,
                        "userName": "liming",
                        "createTime": "2022-10-07 06:16:25",
                        "isDelete": null,
                        "blogId": 1,
                        "parentId": 1,
                        "rootParentId": 1,
                        "child": [
                            {
                                "id": 6,
                                "content": "回復(fù)1-2-1",
                                "userId": 3,
                                "userName": "zhangsan",
                                "createTime": "2022-10-13 06:18:51",
                                "isDelete": null,
                                "blogId": 1,
                                "parentId": 3,
                                "rootParentId": 1,
                                "child": null
                            }
                        ]
                    }
                ]
            }
        ],
        "total": 13
    }
}

對于處于葉子節(jié)點(diǎn)的評(píng)論,其 child 就為 null

前端遞歸顯示

接下來的一個(gè)難題是從后端獲取到的這個(gè)多叉樹結(jié)構(gòu)的數(shù)據(jù)如何顯示出來。

  1. 我們首先能想到的是 Vue 里的 v-for 來循環(huán)輸出所有 comment,再取其 child 進(jìn)行嵌套 v-for 輸出
  2. 但是這樣就會(huì)產(chǎn)生一個(gè)問題,v-for 的嵌套次數(shù)這么寫就是固定的,然而對于這棵多叉樹我們并不知道其深度為多少。舉個(gè)例子,例如我的前端結(jié)構(gòu)是外層一個(gè) v-for 輸出所有的 comment,內(nèi)層一個(gè) v-for 輸出這些 comment 的 child。但是這樣的結(jié)構(gòu)無法輸出 child 的 child,如果再加一層 v-for,又無法輸出 child 的 child 的 child。因?yàn)槲覀儫o法知道這棵樹的深度為多少,所以并不能確定 v-for 的嵌套層樹。而且這樣的一種寫法也實(shí)在是冗余,缺乏優(yōu)雅。
  3. 因此,我們很自然地想到算法中的遞歸。
  4. Vue 中的遞歸可以利用其獨(dú)特的父子組件機(jī)制實(shí)現(xiàn)。簡單來說,Vue 允許父組件調(diào)用子組件,并可進(jìn)行數(shù)據(jù)的傳遞,那么只要我們讓組件自己調(diào)用自己并調(diào)整傳遞的數(shù)據(jù),那么這不就形成了一個(gè)遞歸結(jié)構(gòu)了嗎?

我們接下來來看我的具體實(shí)現(xiàn)

blogDetails.vue(父組件)

<!-- 顯示評(píng)論 -->
<div class="comment-list-container">
	<div class="comment-list-box comment-operate-item">
		<ul class="comment-list" v-for="comment in commentList">
			<!-- 評(píng)論根目錄 -->
			<root :comment="comment" :blog="blog" :getCommentList="getCommentList"></root>
			<!-- 評(píng)論子目錄 -->
			<li class="replay-box" style="display: block;">
				<ul class="comment-list">
					<!-- 子組件遞歸實(shí)現(xiàn) -->
					<child :childComments="comment.child" :parentComment="comment" :blog="blog" :rootParentId="comment.id" :getCommentList="getCommentList" v-if="comment.child != null"></child>
				</ul>
			</li>
		</ul>
	</div>
</div>

在父組件中我們調(diào)用了子組件 child 去實(shí)現(xiàn)評(píng)論的輸出,child 來自于 childComment.vue

childComment.vue

<div class="comment-line-box" v-for="childComment in childComments">
	<div class="comment-list-item">
		<el-avatar icon="el-icon-user-solid" :size="35" style="width: 38px;"></el-avatar>
		<div class="right-box">
			<div class="new-info-box clearfix">
				<div class="comment-top">
					<div class="user-box">
						<span class="comment-name">{{ childComment.userName }}</span>
						<el-tag size="mini" type="danger" v-show="childComment.userName === blog.authorName" style="margin-left: 5px;">作者</el-tag>
						<span class="text">回復(fù)</span>
						<span class="nick-name">{{ parentComment.userName }}</span>
						<span class="date">{{ childComment.createTime }}</span>
						<div class="opt-comment">
							<i class="el-icon-delete"></i>
							<span style="margin-left: 3px;" @click="deleteComment(childComment)">刪除</span>
							<i class="el-icon-chat-round" style="margin-left: 10px;"></i>
							<span style="margin-left: 3px;" @click="showReplay = !showReplay">回復(fù)</span>
						</div>
					</div>
				</div>
				<div class="comment-center">
					<div class="new-comment">{{ childComment.content }}</div>
				</div>
			</div>
		</div>
	</div>
	<!-- 回復(fù)框 -->
	<replay :rootParentId="rootParentId" :comment="childComment" :showReplay="showReplay" :blogId="blogId" :getCommentList="getCommentList" style="margin-top: 5px;"></replay>
	<!-- 嵌套遞歸 -->
	<child :childComments="childComment.child" :parentComment="childComment" :blog="blog" :rootParentId="rootParentId" :getCommentList="getCommentList"></child>
</div>

在子組件中,我們遞歸調(diào)用了自身,并設(shè)置了子評(píng)論和父評(píng)論等數(shù)據(jù)加入下一輪遞歸,由此完成該遞歸過程。

刪除評(píng)論

關(guān)于評(píng)論的操作無非是添加評(píng)論(回復(fù))和刪除評(píng)論。添加評(píng)論比較好理解,只要獲取了相關(guān)的層級(jí)關(guān)系數(shù)據(jù),如 parentId 等,往數(shù)據(jù)表里插入一條記錄就可以了。然而刪除評(píng)論則較為復(fù)雜,刪除評(píng)論不僅要?jiǎng)h除當(dāng)前的這條評(píng)論(回復(fù)),也要?jiǎng)h除其子評(píng)論(回復(fù)),即以該條評(píng)論為根結(jié)點(diǎn)的子樹。

為了能完整地刪除這棵子樹,我們需要遍歷這棵子樹的每一個(gè)結(jié)點(diǎn),比較簡單的方式就是層序遍歷。這里我采用了非遞歸的方法,即借助隊(duì)列實(shí)現(xiàn)。

/**
 * 刪除評(píng)論
 * @param comment
 * @return
 */
@Override
public boolean removeComment(Comment comment) {
    Queue<Comment> queue = new LinkedList<>();
    queue.offer(comment);
    while(!queue.isEmpty()) {
        Comment cur = queue.poll();
        int resultNum = commentMapper.removeById(cur.getId());
        if(resultNum <= 0) return false;
        if(cur.getChild() != null) {
            List<Comment> child = cur.getChild();
            for(Comment tmp: child)
                queue.offer(tmp);
        }
    }
    return true;
}

講到這里差不多就把評(píng)論系統(tǒng)的所有難點(diǎn)講完了!

總結(jié)

到此這篇關(guān)于如何利用Vue+SpringBoot實(shí)現(xiàn)評(píng)論功能的文章就介紹到這了,更多相關(guān)Vue SpringBoot評(píng)論功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA中String類與StringBuffer類的區(qū)別

    JAVA中String類與StringBuffer類的區(qū)別

    這篇文章主要為大家詳細(xì)介紹了JAVA中String類與StringBuffer類的區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • java中的transient關(guān)鍵字解讀

    java中的transient關(guān)鍵字解讀

    這篇文章主要介紹了java中的transient關(guān)鍵字解讀,transient關(guān)鍵字的主要作用就是讓某些被transient關(guān)鍵字修飾的成員屬性變量不被序列化,實(shí)際上也正是因此,在學(xué)習(xí)過程中很少用得上序列化操作,一般都是在實(shí)際開發(fā)中,需要的朋友可以參考下
    2023-09-09
  • Java中四種遍歷List的方法總結(jié)(推薦)

    Java中四種遍歷List的方法總結(jié)(推薦)

    下面小編就為大家?guī)硪黄狫ava中四種遍歷List的方法總結(jié)(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04
  • java實(shí)現(xiàn)excel和txt文件互轉(zhuǎn)

    java實(shí)現(xiàn)excel和txt文件互轉(zhuǎn)

    本篇文章主要介紹了java實(shí)現(xiàn)excel和txt文件互轉(zhuǎn)的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來看下吧
    2017-04-04
  • Java數(shù)據(jù)結(jié)構(gòu)之ArrayList從順序表到實(shí)現(xiàn)

    Java數(shù)據(jù)結(jié)構(gòu)之ArrayList從順序表到實(shí)現(xiàn)

    Java中的ArrayList是一種基于數(shù)組實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu),支持動(dòng)態(tài)擴(kuò)容和隨機(jī)訪問元素,可用于實(shí)現(xiàn)順序表等數(shù)據(jù)結(jié)構(gòu)。ArrayList在內(nèi)存中連續(xù)存儲(chǔ)元素,支持快速的隨機(jī)訪問和遍歷。通過學(xué)習(xí)ArrayList的實(shí)現(xiàn)原理和使用方法,可以更好地掌握J(rèn)ava中的數(shù)據(jù)結(jié)構(gòu)和算法
    2023-04-04
  • Java web xml文件讀取解析方式

    Java web xml文件讀取解析方式

    這篇文章主要介紹了Java web xml文件讀取解析方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • java 使用foreach遍歷集合元素的實(shí)例

    java 使用foreach遍歷集合元素的實(shí)例

    這篇文章主要介紹了java 使用foreach遍歷集合元素的實(shí)例的相關(guān)資料,這里提供實(shí)例幫助大家理解如何使用foreach 進(jìn)行遍歷,希望能幫助到大家,
    2017-08-08
  • SpringBoot MongoDB詳細(xì)使用教程

    SpringBoot MongoDB詳細(xì)使用教程

    這篇文章主要介紹了SpringBoot整合Mongodb實(shí)現(xiàn)簡單的增刪查改,MongoDB是一個(gè)以分布式數(shù)據(jù)庫為核心的數(shù)據(jù)庫,因此高可用性、橫向擴(kuò)展和地理分布是內(nèi)置的,并且易于使用。況且,MongoDB是免費(fèi)的,開源的,感興趣的朋友跟隨小編一起看看吧
    2022-10-10
  • Eureka源碼解析服務(wù)離線狀態(tài)變更

    Eureka源碼解析服務(wù)離線狀態(tài)變更

    這篇文章主要為大家介紹了Eureka源碼解析服務(wù)離線的狀態(tài)變更示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Spring Boot常見外部配置文件方式詳析

    Spring Boot常見外部配置文件方式詳析

    這篇文章主要給大家介紹了關(guān)于Spring Boot常見外部配置文件方式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07

最新評(píng)論