Django-Vue搭建个人博客:发布评论
4357 views, 2021/03/31 updated Go to Comments
经过几个章节的折腾,文章的增删改查也完成得差不多了,这章紧接着开发最后一个大的模块:评论。
入口与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()
方法见过好几回了,用于格式化日期。
发表评论这就搞定了!来看看效果吧。
在文章详情页发表评论如下:
虽然简陋,但该有的功能都有,剩下的就是扩充和美化界面的工作了。
收尾工作
有的读者可能发现了,教程虽然使用了 Vue 3,但是里面用到的核心技术跟 Vue 2 基本没什么不同,甚至可以非常顺滑的互相迁移。那 Vue 3 究竟更新了什么?
下面一章让我们正式进入 Vue 3 最强大的新功能之一:组合式 API。
评论的删改的开发,由于和前面章节所用的技巧高度重合,就不赘述了,留给读者自行研究。