泽恩小站-教程区 Help

发布评论

经过几个章节的折腾,文章的增删改查也完成得差不多了,这章紧接着开发最后一个大的模块: 评论

入口与Props

作为一个普通的博客,评论通常位于文章详情的末尾 ,以便读者发表对博主的赞赏之情。又因为评论模块和文章模块本身没太多交联,比较独立,因此可以不让它们的代码搅在一起。

因此修改 ArticleDetail.vue ,新增一个 Comments 组件 :(注意此组件还没写)

<!-- frontend/src/views/ArticleDetail.vue --> <template> ... <Comments :article="article" /> <BlogFooter/> </template> <script> ... import Comments from '@/components/Comments.vue' export default { ... components: {BlogHeader, BlogFooter, Comments}, ... } </script> ...

Props 可以是数字、字符串等原生类型,也可以是如 article 这类自定义对象 。传递文章对象是为了让评论组件获取到相关联文章的所有评论。

评论组件

接下来正式写评论组件。

新建 frontend/src/components/Comments.vue ,写入代码:

<!-- frontend/src/components/Comments.vue --> <template> <br><br> <hr> <h3>发表评论</h3> <!-- 评论多行文本输入控件 --> <textarea v-model="message" :placeholder="placeholder" name="comment" id="comment-area" cols="60" rows="10" ></textarea> <div> <button @click="submit" class="submitBtn">发布</button> </div> <br> <p>已有 {{ comments.length }} 条评论</p> <hr> <!-- 渲染所有评论内容 --> <div v-for="comment in comments" :key="comment.id" > <div class="comments"> <div> <span class="username"> {{ comment.author.username }} </span> 于 <span class="created"> {{ formatted_time(comment.created) }} </span> <span v-if="comment.parent"> 对 <span class="parent"> {{ comment.parent.author.username }} </span> </span> 说道: </div> <div class="content"> {{ comment.content }} </div> <div> <button class="commentBtn" @click="replyTo(comment)">回复</button> </div> </div> <hr> </div> </template> <script> import axios from 'axios'; import authorization from '@/utils/authorization'; export default { name: 'Comments', // 通过 props 获取当前文章 props: { article: Object }, data: function () { return { // 所有评论 comments: [], // 评论控件绑定的文本和占位符 message: '', placeholder: '说点啥吧...', // 评论的评论 parentID: null } }, // 监听 article 对象 // 以便实时更新评论 watch: { article() { this.comments = this.article !== null ? this.article.comments : [] } }, methods: { // 提交评论 submit() { const that = this; authorization() .then(function (response) { if (response[0]) { axios .post('/api/comment/', { content: that.message, article_id: that.article.id, parent_id: that.parentID, }, { headers: {Authorization: 'Bearer ' + localStorage.getItem('access.myblog')} }) .then(function (response) { // 将新评论添加到顶部 that.comments.unshift(response.data); that.message = ''; alert('留言成功') }) } else { alert('请登录后评论。') } }) }, // 对某条评论进行评论 // 即二级评论 replyTo(comment) { this.parentID = comment.id; this.placeholder = '对' + comment.author.username + '说:' }, // 修改日期显示格式 formatted_time: function (iso_date_string) { const date = new Date(iso_date_string); return date.toLocaleDateString() + ' ' + date.toLocaleTimeString() }, } } </script> <style scoped> button { cursor: pointer; border: none; outline: none; color: whitesmoke; border-radius: 5px; } .submitBtn { height: 35px; background: steelblue; width: 60px; } .commentBtn { height: 25px; background: lightslategray; width: 40px; } .comments { padding-top: 10px; } .username { font-weight: bold; color: darkorange; } .created { font-weight: bold; color: darkblue; } .parent { font-weight: bold; color: orangered; } .content { font-size: large; padding: 15px; } </style>

实际上没有啥新知识,都是前面章节技巧的混合:

  • 组件通过 Props 获取了文章对象 ,利用 watch 监听此对象并实时更新关联评论。注意这里不能通过 mounted() 去实现此逻辑,原因是因为挂载 Vue 实例的时候 article初始值null

  • 提交评论用 submit() 方法,后端若返回成功则将最新的评论更新this.comments 中。

  • replyTo() 方法用于记录评论的父级(即“评论的评论”),如果为 null 则表示此评论自己就是第一级。

  • formatted_time() 方法见过好几回了,用于格式化日期。

发表评论这就搞定了!来看看效果吧。

在文章详情页发表评论如下:

Drf p310 1

虽然简陋,但该有的功能都有,剩下的就是扩充和美化界面的工作了。

收尾工作

有的读者可能发现了,教程虽然使用了 Vue 3,但是里面用到的核心技术跟 Vue 2 基本没什么不同,甚至可以非常顺滑的互相迁移。 那 Vue 3 究竟更新了什么?

下面一章让我们正式进入 Vue 3 最强大的新功能之一:组合式 API。

Last modified: 06 January 2025