Vue.js同路由下无需刷新页面更新评论的实现方案
问题描述
我开发了一个博客应用,添加评论功能后,必须硬刷新博客详情路由才能更新评论展示。以下是我的博客详情路由组件、评论添加组件、评论展示组件的代码,请问如何实现无需硬刷新页面即可更新评论?
博客详情路由组件代码
<template> <div> <h3 class="pt-4"> {{blog.title}}</h3> <p class="pt-3"> {{blog.description}} </p> <Review /> <reviewSection/> </div> </template> <script> import axios from "axios"; import Review from "../components/Review.vue"; import reviewSection from "../components/reviewSection.vue"; export default { components: { }, data() { return { blog: [] }; }, watch: {}, created() { axios .get(`http://localhost:4000/api/blogs/${this.$route.params.id}`, {}) .then(response => { this.blog = response.data.blog; }) .catch(error => { console.log(error); }); }, }; </script>
评论添加组件代码
<template> <div class="a-spacing-base"> <textarea placeholder="Add a comment" v-model="body" ></textarea> </div> <div class="a-row text-right a-spacing-top-large"> <button class="a-button-text btn" @click="onAddReview" >Submit</button> </div> </template> <script> import axios from "axios"; export default { data() { return { body: "", }; }, methods: { async onAddReview() { try { var data = { body: this.body, }; let response = await axios.post( `http://localhost:4000/api/reviews/${this.$route.params.id}`, data, ); } catch (err) { console.log(err); } } } }; </script>
评论展示组件代码
<template> <div> <div v-for="review in reviews" :key="review._id" class="pt-3" > <div class="row"> <div class="pt-2"> {{ review.body }} </div> </div> </div> </template> <script> import axios from "axios"; export default { data(){ return { reviews: null, }; }, watch: { "$route.params.id": { handler(newVal) { this.loadReviews(newVal); } } }, methods: { loadReviews(id) { axios .get(`http://localhost:4000/api/reviews/${id}`, {}) .then(response => { // console.log(response); this.reviews = response.data.reviews; }) .catch(error => { console.log(error); error; }); }, } }; </script>
解决方案
核心问题是评论提交后,评论展示组件无法感知新评论的生成,需要在提交成功后触发评论列表的更新。以下是三种可行方案:
方案一:事件总线跨组件通知更新
适合组件无直接父子关系的场景,通过全局事件总线传递更新信号。
- 全局注册事件总线
在项目入口文件(如main.js)中添加:
Vue.prototype.$eventBus = new Vue()
- 修改评论添加组件
提交成功后触发自定义事件,传递当前博客ID:
<script> import axios from "axios"; export default { data() { return { body: "", }; }, methods: { async onAddReview() { try { var data = { body: this.body, }; await axios.post( `http://localhost:4000/api/reviews/${this.$route.params.id}`, data, ); // 触发评论更新事件 this.$eventBus.$emit('reviewAdded', this.$route.params.id) // 清空输入框 this.body = '' } catch (err) { console.log(err); } } } }; </script>
- 修改评论展示组件
监听事件总线的更新信号,重新加载评论,并补充初始加载逻辑:
<script> import axios from "axios"; export default { data(){ return { reviews: null, }; }, created() { // 页面加载时初始化评论列表 this.loadReviews(this.$route.params.id) // 监听评论更新事件 this.$eventBus.$on('reviewAdded', (blogId) => { this.loadReviews(blogId) }) }, beforeDestroy() { // 销毁组件时移除监听,避免内存泄漏 this.$eventBus.$off('reviewAdded') }, watch: { "$route.params.id": { handler(newVal) { this.loadReviews(newVal); } } }, methods: { loadReviews(id) { axios .get(`http://localhost:4000/api/reviews/${id}`, {}) .then(response => { this.reviews = response.data.reviews; }) .catch(error => { console.log(error); }); }, } }; </script>
方案二:直接添加新评论到列表(无需重新请求)
如果后端返回新创建的评论数据,可直接将其插入前端列表,减少HTTP请求次数。
- 修改评论添加组件
提交成功后传递新评论数据:
<script> import axios from "axios"; export default { data() { return { body: "", }; }, methods: { async onAddReview() { try { var data = { body: this.body, }; const response = await axios.post( `http://localhost:4000/api/reviews/${this.$route.params.id}`, data, ); // 假设后端返回的新评论在response.data.review中 this.$eventBus.$emit('newReview', response.data.review) this.body = '' } catch (err) { console.log(err); } } } }; </script>
- 修改评论展示组件
监听新评论事件,直接添加到列表:
<script> import axios from "axios"; export default { data(){ return { reviews: [], }; }, created() { this.loadReviews(this.$route.params.id) this.$eventBus.$on('newReview', (newReview) => { this.reviews.push(newReview) }) }, beforeDestroy() { this.$eventBus.$off('newReview') }, watch: { "$route.params.id": { handler(newVal) { this.loadReviews(newVal); } } }, methods: { loadReviews(id) { axios .get(`http://localhost:4000/api/reviews/${id}`, {}) .then(response => { this.reviews = response.data.reviews; }) .catch(error => { console.log(error); }); }, } }; </script>
方案三:父组件传递更新方法(父子通信)
利用博客详情组件作为父容器,直接调用子组件的更新方法。
- 修改博客详情组件
注册子组件,添加ref引用并定义刷新方法:
<template> <div> <h3 class="pt-4"> {{blog.title}}</h3> <p class="pt-3"> {{blog.description}} </p> <!-- 绑定自定义事件 --> <Review @review-submitted="refreshReviews"/> <!-- 添加ref引用子组件 --> <reviewSection ref="reviewSection"/> </div> </template> <script> import axios from "axios"; import Review from "../components/Review.vue"; import reviewSection from "../components/reviewSection.vue"; export default { // 注册子组件 components: { Review, reviewSection }, data() { return { blog: [] }; }, created() { axios .get(`http://localhost:4000/api/blogs/${this.$route.params.id}`, {}) .then(response => { this.blog = response.data.blog; }) .catch(error => { console.log(error); }); }, methods: { refreshReviews() { // 调用评论展示组件的加载方法 this.$refs.reviewSection.loadReviews(this.$route.params.id) } } }; </script>
- 修改评论添加组件
提交成功后触发父组件的自定义事件:
<script> import axios from "axios"; export default { data() { return { body: "", }; }, methods: { async onAddReview() { try { var data = { body: this.body, }; await axios.post( `http://localhost:4000/api/reviews/${this.$route.params.id}`, data, ); // 通知父组件刷新评论 this.$emit('review-submitted') this.body = '' } catch (err) { console.log(err); } } } }; </script>
- 修改评论展示组件
补充初始加载逻辑:
<script> import axios from "axios"; export default { data(){ return { reviews: null, }; }, created() { // 页面初始化时加载评论 this.loadReviews(this.$route.params.id) }, watch: { "$route.params.id": { handler(newVal) { this.loadReviews(newVal); } } }, methods: { loadReviews(id) { axios .get(`http://localhost:4000/api/reviews/${id}`, {}) .then(response => { this.reviews = response.data.reviews; }) .catch(error => { console.log(error); }); }, } }; </script>
补充说明
- 原博客详情组件未注册子组件,需在
components选项中添加Review和reviewSection,否则组件无法正常渲染。 - 原评论展示组件未在
created钩子中加载初始评论,补充后才能确保进入页面时显示已有评论。
内容的提问来源于stack exchange,提问作者jibzy
相关产品推荐
相关产品推荐

