在使用 Vue 进行单页面应用(SPA)开发时,遇到页面更新但不刷新的问题是常见的。这通常是由于 Vue 路由的导航守卫功能导致的。这篇文章将介绍如何解决这个问题,并提供示例代码以供参考。
问题分析
当使用 Vue Router 进行页面跳转时,如果当前页面和跳转页面的组件名称相同,则不会触发组件的 beforeRouteUpdate
钩子函数。这将导致在组件内通过异步请求获取的数据更新后,页面不会主动刷新,而是需要手动刷新页面才能看到更新后的数据。
下面我们通过一个简单的例子来说明这个问题。
假设我们有一个公共的 NavBar
组件,这个组件会在每个页面都呈现并获取用户登录状态。我们在其中通过 AJAX 请求获取用户信息,并根据用户角色控制导航栏展示项。
// javascriptcn.com 代码示例 <template> <nav> <ul> <li v-if="isAdmin">管理</li> <li>首页</li> <li>文章</li> <li v-if="isLogin">注销</li> <li v-else>登录</li> </ul> </nav> </template> <script> export default { data() { return { isAdmin: false, isLogin: false, }; }, async created() { const user = await this.fetchUser(); this.isAdmin = user.role === "admin"; this.isLogin = user.isLogin; }, methods: { async fetchUser() { const response = await fetch("/api/user"); return await response.json(); }, }, }; </script>
然后我们有两个路由页面:首页和文章页面,两个页面都显示一些数据,并可以通过异步请求更新数据。
// javascriptcn.com 代码示例 <template> <div> <nav-bar></nav-bar> <h1>首页</h1> <div>{{ content }}</div> <button @click="refreshData">刷新数据</button> </div> </template> <script> export default { data() { return { content: "", }; }, created() { this.fetchData(); }, methods: { async fetchData() { const response = await fetch("/api/home"); const data = await response.json(); this.content = data.content; }, refreshData() { this.content = ""; this.fetchData(); }, }, }; </script>
// javascriptcn.com 代码示例 <template> <div> <nav-bar></nav-bar> <h1>文章</h1> <ul> <li v-for="item in list" :key="item.id"> <span>{{ item.title }}</span> <button @click="deleteItem(item.id)">删除</button> </li> </ul> <button @click="addItem">新增文章</button> </div> </template> <script> export default { data() { return { list: [], }; }, async created() { this.fetchList(); }, methods: { async fetchList() { const response = await fetch("/api/articles"); const data = await response.json(); this.list = data.list; }, deleteItem(id) { // send delete request }, addItem() { // navigate to item detail page }, }, }; </script>
现在我们注意到一个问题,在从首页跳转到文章页面并删除一篇文章后,页面不会自动刷新,而是停留在当前页面。这是因为文章页面未能监听到当前路由发生了变化。
解决方案
要解决这个问题,我们需要在路由跳转时手动触发组件的 beforeRouteUpdate
钩子函数。这个函数在组件所在的路由被更新时调用,我们可以在其中触发一些自定义逻辑,例如刷新组件数据。
为了让每个页面都能响应路由更新的事件,我们可以在路由配置中为每个页面添加一个 beforeEnter
钩子函数。
// javascriptcn.com 代码示例 const router = new VueRouter({ routes: [ { path: "/home", component: Home, beforeEnter: (to, from, next) => { EventBus.$emit("route-updated"); next(); }, }, { path: "/articles", component: Articles, beforeEnter: (to, from, next) => { EventBus.$emit("route-updated"); next(); }, }, ], });
这里我们使用了一个事件总线 EventBus
来触发路由更新事件。在组件中监听 route-updated
事件,并在 beforeRouteUpdate
钩子函数中调用组件的自定义方法更新组件数据。
// javascriptcn.com 代码示例 // main.js export const EventBus = new Vue(); // Articles.vue export default { // ... created() { EventBus.$on("route-updated", this.refreshData); }, beforeRouteUpdate(to, from, next) { this.fetchList(); next(); }, methods: { async fetchList() { const response = await fetch("/api/articles"); const data = await response.json(); this.list = data.list; }, // ... }, };
至此,我们已经成功地解决了 Vue SPA 更新页面不刷新的问题。
总结
通过本文的介绍,我们了解了 Vue SPA 更新页面不刷新的原因,并提出了一种解决方案,利用 Vue Router 的 beforeEnter
钩子函数和事件总线来触发路由更新事件,使页面能够自动刷新。希望本文对您的学习和工作有所帮助。完整的示例代码见下:
// javascriptcn.com 代码示例 // main.js import Vue from "vue"; import VueRouter from "vue-router"; import { EventBus } from "./EventBus"; import Home from "./Home"; import Articles from "./Articles"; const router = new VueRouter({ routes: [ { path: "/home", component: Home, beforeEnter: (to, from, next) => { EventBus.$emit("route-updated"); next(); }, }, { path: "/articles", component: Articles, beforeEnter: (to, from, next) => { EventBus.$emit("route-updated"); next(); }, }, ], }); Vue.use(VueRouter); new Vue({ el: "#app", router, template: "<router-view></router-view>", }); // Home.vue <template> <div> <nav-bar></nav-bar> <h1>首页</h1> <div>{{ content }}</div> <button @click="refreshData">刷新数据</button> </div> </template> <script> import { EventBus } from "./EventBus"; export default { data() { return { content: "", }; }, created() { EventBus.$on("route-updated", this.refreshData); this.fetchData(); }, methods: { async fetchData() { const response = await fetch("/api/home"); const data = await response.json(); this.content = data.content; }, refreshData() { this.content = ""; this.fetchData(); }, }, }; </script> // Articles.vue <template> <div> <nav-bar></nav-bar> <h1>文章</h1> <ul> <li v-for="item in list" :key="item.id"> <span>{{ item.title }}</span> <button @click="deleteItem(item.id)">删除</button> </li> </ul> <button @click="addItem">新增文章</button> </div> </template> <script> import { EventBus } from "./EventBus"; export default { data() { return { list: [], }; }, created() { EventBus.$on("route-updated", this.fetchList); this.fetchList(); }, beforeRouteUpdate(to, from, next) { this.fetchList(); next(); }, methods: { async fetchList() { const response = await fetch("/api/articles"); const data = await response.json(); this.list = data.list; }, deleteItem(id) { // send delete request }, addItem() { // navigate to item detail page }, }, }; </script>
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652bd8067d4982a6ebdb2cc0