Vue+SignalR场景下异步获取新数据时v-for绑定列表未更新问题咨询
问题原因&解决方案
核心问题:SignalR回调的this指向错误
你当前的UpdateAccount事件回调使用了普通函数声明,函数内部的this指向的是SignalR连接实例,根本不是Vue实例,所以你写的this.account = accountData并没有修改到Vue内部的响应式数据,自然不会触发页面重新渲染。你打印出来的是外部作用域的accountData变量,和Vue的响应式数据没有绑定关联。
修复方案
方案1(推荐):把SignalR逻辑移入Vue实例内部
将SignalR的初始化、事件监听都放到Vue的mounted生命周期钩子中,回调使用箭头函数保证this指向Vue实例,修改后的JS代码如下:
<script type="module" async> import { server } from "./assets/js/config.js"; import { getProject } from "./assets/js/saleproject.js"; import { getAccountById } from "./assets/js/account.js"; import { createApp } from 'https://unpkg.com/vue@3.2.4/dist/vue.esm-browser.js' // 从querystring获取accountId let accountId = getQueryStringByParameter("accountId"); // 调用API加载初始数据 var accountData = await getAccountById(accountId); // 项目相关逻辑 let projectData = null if (accountData.lastOrder.saleProjectCode != null) { projectData = await getProject(accountData.lastOrder.saleProjectCode) console.log('Sale Project') console.table(accountData.lastOrder) } // 计算总金额 let totalDue = projectData ? (accountData.lastOrder.amount - accountData.lastOrder.amountFilled) * projectData.salePrice : 0; function getQueryStringByParameter(name) { name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var regex = new RegExp("[\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } // Vue实例创建 createApp({ data: () => ({ account: accountData, walletValid: accountData.account.isValidWithdrawAddress, depositAddress: server + "/qr/" + accountData.account.depositAddress, clipboardDepositAddress: accountData.account.depositAddress, salePrice: totalDue, connection: null // 将SignalR连接存到Vue实例上方便后续管理 }), methods: { copyDepositAddress() { navigator.clipboard.writeText(this.clipboardDepositAddress) } }, async mounted() { // 初始化SignalR连接 this.connection = new signalR.HubConnectionBuilder() .withUrl(server + "/statusHub") .withAutomaticReconnect() .build(); await this.connection.start() try { await this.connection.invoke("AddToGroup", accountId) } catch (err) { console.error(err.toString()) } // 回调使用箭头函数,this自动指向Vue实例 this.connection.on("UpdateAccount", async (currentAccountId, reason) => { const newAccountData = await getAccountById(accountId); // 直接修改Vue的响应式数据自动触发渲染 this.account = newAccountData; console.log("Update from signalr: " + reason + ' ' + currentAccountId); }) this.connection.onclose(async () => { await this.connection.start(); }); } }).mount("#js-account"); </script>
方案2(快速修复无需移动代码):把Vue实例存为变量直接修改
如果你不想改动现有SignalR的代码位置,可以把创建好的Vue实例存到一个变量中,在回调中直接修改这个变量上的account属性即可:
// 先存储创建好的Vue实例 const vueApp = createApp({ // 原有Vue配置保持不变 }).mount("#js-account"); // 原有SignalR回调修改为如下内容 connection.on( "UpdateAccount", async function (currentAccountId, reason) { accountData = await getAccountById(accountId); // 直接修改Vue实例的响应式数据 vueApp.account = accountData; } );
额外校验
你当前v-for使用deposit.id作为key是符合要求的,不会因为key的问题导致列表不更新,修改完指向问题后即可正常触发渲染。
内容的提问来源于stack exchange,提问作者ImranR
相关产品推荐
相关产品推荐

