原文链接:《Vue数据更新了但是视图不更新》
最近在发生了一件很奇怪的事,明明Vue中data的数据已经更新了,但是页面渲染出来的内容并没有跟随着更新,包括使用elemeUI的时候也有这种情况!难道是数据双向绑定出问题了?
经过我东查查西找找,终于是找到了原因和解决方法,为了避免以后找不到,那就在这里记录一下吧!
可能出现的原因
通过集成各家所讲,本人简单罗列了可能是以下原因导致的,当然,每个人遇到的情况不一样,甚至还会遇到更加邪门的事情,还需要根据自己的实际情况去排查。
1. 数据响应性问题
Vue无法检测到对象属性的添加或删除。例如,如果添加了一个新的属性到已经存在的对象上,Vue将不会触发视图更新。
利用索引直接设置一个项时,例如 vm.items[indexOfItem] = newValue
。
数组数据变动,使用某些方法操作数组,变动数据时,有些方法无法被vue监测:
push()
、pop()
、splice()
、sort()
、reverse()
可被vue检测到。filter()
、concat()
、slice()
这些不会改变原始数组,但总是返回一个新数组。
2. 异步更新队列
Vue在更新DOM时是异步执行的。当数据变化时,Vue不会立即更新DOM,而是等到下一次事件循环再执行更新。如果需要立即更新视图,可以使用 Vue.nextTick(callback)
方法。
3. 使用v-if条件渲染
如果元素是通过 v-if
条件渲染的,并且条件在初始渲染时是 false
,当条件变为 true
时,元素可能不会立即出现。确保条件逻辑正确。
4. 缓存问题
如果使用了某些缓存机制,如 keep-alive
,那就得看看是不是有处理了组件的激活和停用状态。
开始排查
首先在控制台中打印出最后更新的数据,得出的结果是正确的,也就是说在js中并没有存在处理上的问题。
然后是使用简单粗暴的方法,先注释掉对应的页面标签,然后再放开注释,这个时候惊喜发现数据更新了!
这个时候就需要进一步去测试,所以我定义了一个变量为布尔值,在父级元素中使用 v-if
去绑定这个布尔值,然后再数据赋值之前将布尔值设置为 false
,然后在数据赋值后将这个布尔值改成 true
,使用 v-if
这个操作就实现了类似于刚才的注释操作。
这里补充一下,
v-if
和v-show
虽然在视觉上看是一样的效果,但是v-if
是将对应的元素进行注释操作,简单直白的说就是销毁了这个元素,而v-show
只是对该元素设置了CSS中的display: none;
的属性,实际上这个元素还是存在在页面中。
其实到这一步已经解决了视图不更新的问题,但是感觉这样子操作并不优雅!因为在使用 v-if
的时候,虽然在浏览器上处理的很快,但有个问题就是再快也快不过眼睛,在频繁使用 v-if
操作的时候就会看到这个页面元素就跟天上的星星那样一闪一闪的。这显然对用户体验是非常不友好的。
所以就拿出了以下“法宝”!
this.$set()
this.$set
是 Vue.js 中的一个方法,用于解决Vue不能检测到对象属性的添加或删除的问题。当需要动态地向响应式对象添加一个属性,并确保这个新属性同样是响应式的(即当其值改变时,视图也会更新),可以使用 this.$set
方法。
在Vue2中,this.$set
的常见用法如下:
this.$set(target, propertyName, value)
- target:要添加属性的对象。
- propertyName:要添加的属性的名称(字符串形式)。
- value:要设置的新属性值。
例如,当前有一个响应式对象obj,然后在此基础上添加一个名为newProp
的新属性:
new Vue({
data: {
obj: {
existingProp: 'some value'
}
},
methods: {
addNewProp() {
this.$set(this.obj, 'newProp', 'newValue');
}
}
});
在调用 addNewProp
方法后,obj现在将包含一个名为 newProp
的新属性,且该属性是响应式的。如果直接通过 obj.newProp = 'newValue'
来添加属性,那么这个新属性将不会是响应式的。
而在Vue3中,this.$set
已被移除,因为Vue3使用了 Proxy
来实现响应式系统,这样在数据变化后能够更精确地检测到属性的添加和删除。
this.$forceUpdate()
this.$forceUpdate()
是 Vue.js 中的一个实例方法,用于强制Vue实例重新渲染页面。在Vue.js中,当组件或其数据发生变化时,Vue会自动检测这些变化并重新渲染页面。然而,在某些情况下,Vue可能无法检测到数据的变化,因此不会触发重新渲染,使用 this.$forceUpdate()
方法来手动触发重新渲染。
使用 this.$forceUpdate()
的步骤如下:
- 在组件中定义需要更新的数据。
- 在组件的方法中使用 this.$forceUpdate()。
export default {
data() {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
this.$forceUpdate();
}
}
};
但是!this.$forceUpdate()
应该是一种较少使用的方法,因为这个方法破坏了Vue.js的响应性系统。在大多数情况下,除非只有在Vue.js不能自动检测到数据变化并重新渲染的情况下,才应该使用 this.$forceUpdate()
。
讲到这里,基本上原因和处理方案都罗列的差不多了,这个时候只需要根据实际情况去选择对应的方法去处理。个人观点,无论是用哪种方法,首先考虑的还是用户体验,其次才是处理方法优化,完毕!