Vue2长轮询更新数据无变化但watch仍触发问题求解
Vue2长轮询更新状态时watch重复触发解决方案
问题根因为引用类型赋值触发watch的机制导致的:每次请求返回后你直接将ffGameState赋值为全新的response.data对象,哪怕两个对象内容完全一致,内存引用地址已经发生变化。Vue2监听引用类型时,只要引用地址变更就会触发watch回调,你打印时看到val和oldVal内容一致,是因为Vue2在引用类型的watch回调中,oldVal和val指向同一个引用,并非真的内容没有变更。
可选解决方案
方案1:更新状态前做全局值对比
仅当接口返回的新数据和当前状态内容不一致时才赋值,从根源避免无意义的更新触发:
loadFfGameState() { this.$http .get("/gamestates/ff") .then(response => { // 用lodash的isEqual做深度内容对比,不一致才更新 if (!_.isEqual(response.data, this.ffGameState)) { this.ffGameState = response.data } }) .then(() => { setTimeout(() => this.loadFfGameState(), 100) }) }
该方案改动量最小,适合状态字段不多的场景。
方案2:watch回调内加内容校验
如果不想修改请求更新逻辑,直接在watch中增加判断,内容无变化时直接跳过业务逻辑:
watch: { "ffGameState.errors.team1": { deep: true, handler: function(val, oldVal) { // 内容一致直接返回,不执行后续逻辑 if (_.isEqual(val, oldVal)) return // 原有业务逻辑写在下方 console.log("change detected") console.log(val) } } }
方案3:精细化更新对应字段
不需要整对象替换ffGameState,仅单独更新需要变动的字段,更新前对该字段做内容对比:
loadFfGameState() { this.$http .get("/gamestates/ff") .then(response => { const newTeam1 = response.data?.errors?.team1 // 仅对比你监听的team1字段,性能更优 if (newTeam1 && !_.isEqual(newTeam1, this.ffGameState.errors.team1)) { this.ffGameState.errors.team1 = newTeam1 } // 其他需要更新的字段参照该逻辑单独处理 }) .then(() => { setTimeout(() => this.loadFfGameState(), 100) }) }
该方案性能最优,适合状态字段多、仅部分字段需要监听变更的场景。
内容的提问来源于stack exchange,提问作者LocoMH
相关产品推荐
相关产品推荐

