# Vue_Comment_Component
**Repository Path**: moxi159753/Vue_Comment_Component
## Basic Information
- **Project Name**: Vue_Comment_Component
- **Description**: A comment component written using the vue and Ant Design of Vue
- **Primary Language**: JavaScript
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 4
- **Forks**: 1
- **Created**: 2020-01-04
- **Last Updated**: 2024-04-06
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Vue制作评论组件
## 项目启动
```
yarn install
```
```
yarn run serve
```
## 前言
一直想着重写蘑菇博客的评论功能,但是一直之前一直没有头绪,最比较让人头疼的是前端的样式问题,最近在看蚂蚁的UI框架的时候,偶然看到了评论组件:[点我传送](https://www.antdv.com/components/comment-cn/)

瞬间感觉发现了新大陆,但是相对来说功能还有些简单,无法满足博客的要求,所以想办法改造一下,下面看最终封装好的组件为:
源码地址为:https://gitee.com/moxi159753/Vue_Comment_Component

## 封装CommentList组件
为了实现多级递归渲染,首先要做的是,将单条评论封装成一个组件,然后里面重复调用自身
```
```
在这里需要用到的一个非常重要的字段就是:
```
name:"CommentList",
```
如果我们想要在该组件中嵌套本身,那么直接就可以在模板中使用name属性值 递归调用自身,之前一直不太明白name字段是做什么用处的,有的时候可以加,有的时候删除也可以,今天才了解是为了在组件中调用自己的时候就能用得上。
## 在父组件中调用该CommentList组件
首先我们需要定义一些多级评论的数据,这里的数据是多层嵌套的,通过reply字段
```
comments: [
{
uid: 'uid000',
userName: "陌溪",
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
content: '我是一级评论',
reply: [
{
uid: 'uid001',
userName: "陌溪",
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
content: '我是二级评论',
reply: [
{
uid: 'uid002',
userName: "陌溪",
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
content: '我是三级评论',
reply: []
}
]
}, {
uid: 'uid003',
userName: "陌溪",
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
content: '我是二级评论',
reply: []
}
]
},
{
uid: 'uid004',
userName: "陌溪",
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
content: '我是一级评论',
reply: [
{
uid: 'uid005',
userName: "陌溪",
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
content: '我是二级评论',
reply: []
}
]
},
],
```
然后我们需要引入刚刚定义的CommentList子组件,传递进对应的评论数据,完整的代码如下所示:
```html
```
到目前为止,我们渲染出来的评论模块已经具备层次关系了

下面我们需要做的就是引入回复模块
## 封装CommentBox组件
ant design也提供了回复框,如下所示

我们将其封装成对应的组件 CommentBox
```html
```
然后在CommentList中引入对应的模块,具体需要做的事情是,在回复按钮出设置点击时间,当我们点击的时候,将对应的评论渲染出来,我们首先将CommentBox设置为不显示
```
```
然后当我们点击的时候,就需要将我们点击的comment设置为显示状态,其它的设置为none
```
replyTo: function (uid) {
var lists = document.getElementsByClassName("comment");
for (var i = 0; i < lists.length; i++) {
lists[i].style.display = 'none';
}
document.getElementById(uid).style.display = 'block';
this.replyInfo.replyUid = uid
},
```
我们通过document.getElementByClassName把所有的CommentBox获取到来,然后设置成none,同时将我们点击的CommentBox设置成显示即可
运行效果如下所示:

## 引入Vuex
通过上面所述,我们就能完成点击回复,弹出对话框,然后输入对话框的内容后,我们还需要回显到页面上
但是这样的方式存在一个问题,就是通过this.$emit和父组件通信的时候,父组件只有在一级评论时,才能够监听到子组件的改变,所以后面就改变了策略,采用vuex存储data数据,也就是评论数据
首先创建如下文件:

app.js文件内容如下,该文件主要是编写操作store的一些方法
```
import {SET_COMMENT_LIST, INCREMENT} from "./mutation-types";
const app = {
// 全局状态
state: {
commentList: [],
id: 100,
},
// getters是对数据的包装,例如对数据进行拼接,或者过滤
getters: {
//类似于计算属性
// 增加的方法
},
// 如果我们需要更改store中的状态,一定要通过mutations来进行操作
mutations: {
// 传入自定义参数
[SET_COMMENT_LIST](state, commentList) {
state.commentList = commentList
},
[INCREMENT](state) {
state.id += 1
},
},
// actions是我们定义的一些操作,正常情况下,我们很少会直接调用mutation方法来改变state
actions: {
}
}
export default app
```
mutation-types.js文件,用来定义一些常量
```
export const SET_COMMENT_LIST = "setCommentList"
export const INCREMENT = "increment"
export const DECREMENT = "decrement"
```
index.js,如果是多模块,则可以在这里进行配置
```
import Vue from 'vue'
import Vuex from 'vuex'
import app from './app'
//让vuex生效
Vue.use(Vuex)
const store = new Vuex.Store({
// 将app和user放在store中
modules: {
app
}
})
export default store
```
然后修改最新的CommentList组件
```
```
这里Vuex参考了之前的一篇博客:[Vuex学习指南-实现一个计数器](http://moguit.cn/#/info?blogUid=a00ebe1473c584ff94bdd40402a4d573)
主要实现思路是:首先当用户在CommentBox中输入内容,并且提交后,触发CommentList中的submitBox方法,该方法传入参数e,也就是发送的评论,然后我们首先判断是否是一级评论
```
submitBox(e) {
// 一级评论
if (e.replyUid == 0) {
var firstComment = this.$store.state.app.commentList;
firstComment.push(e);
this.$store.commit("setCommentList", firstComment);
this.$store.commit("increment");
return;
}
document.getElementById(e.replyUid).style.display = 'none'
var comments = this.$store.state.app.commentList;
this.getMenuBtnList(comments, e.replyUid, e)
this.$store.commit("setCommentList", comments);
this.$store.commit("increment");
},
```
通过replyUid来判断,如果replyUid为0,说明该评论是一级评论,那么直接从store中获取,关于store中的commentList,是在index.vue页面,初始化的时候填入的
```
mounted() {
this.setCommentList(this.comments);
},
```
如果是一级评论,我们直接把评论追加到commentList中,然后通过
```
this.$store.commit("setCommentList", comments);
this.$store.commit("increment");
```
更新CommentList列表的数据,那么页面就会重新渲染,也就看到我们新加入的评论了,同时我们的id也需要变换,id的改变,是为了唯一表示CommentBox,这样我们就可以通过以下代码
```
document.getElementById(e.replyUid).style.display = 'none'
```
来控制评论box的显示与否,同时当不是一级评论的时候,我们需要通过递归,找到该评论的父ID,这个方法需要三个参数,一个是评论列表,一个是 父评论Uid,一个是需要填充的评论,那么执行该方法后,会自动将评论填充到指定的父评论的reply下
```
updateCommentList(commentList, uid, targetComment) {
if (commentList == undefined || commentList.length <= 0) {
return;
}
for (let item of commentList) {
console.log("递归中", item.uid, uid)
if (item.uid === uid) {
var menu = item.reply;
menu.push(targetComment);
} else {
this.updateCommentList(item.reply, uid, targetComment);
}
}
}
```
index.vue完整代码,如下
```
```