今天遇到一个小问题,又是关于vue数据渲染的。
选中模块分类时,带出业务类型大类的数据。但是写完之后数据没有带出,打印跟使用vue-devtools调试发现数据已经渲染,就明白又是数据渲染的问题。但是以前遇到这种问题,一般是对象或数组进行深层次的嵌套才会出现,这次就一个空对象,searchDataList: {}
竟然也出现了这种问题。
先不慌,这种问题遇到了很多次,解决办法也有问多。
首先贴上简写的代码:
getChildMedia() {
this.searchDataList.mediaBigCategory = []
if(模块分类数据存在时) {
req.get(模块分类).then(res => {
this.searchDataList.mediaBigCategory = res
})
}
}
1.this.$set()
首先使用了this.$set(),这是最便捷最常用的方法。对于这个用法,官方的解释是
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = ‘hi’)
所以我将代码修改成:
getChildMedia() {
this.searchDataList.mediaBigCategory = []
if(模块分类数据存在时) {
req.get(模块分类).then(res => {
this.$set(this.searchDataList, 'mediaBigCategory', res)
//this.searchDataList.mediaBigCategory = res
})
}
}
事实证明毫无效果。
2.this.$nextTick()
这也是一种常用方法,先来看下官方解释:
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
然后我将代码修改为
getChildMedia() {
this.searchDataList.mediaBigCategory = []
if(模块分类数据存在时) {
req.get(模块分类).then(res => {
this.$nextTick(() => {
this.searchDataList.mediaBigCategory = res
})
})
}
}
实时证明依旧无效!
3.this.$forceUpdate()
这是一种不太常用方法,看下官方解释:
迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
修改代码如下:
getChildMedia() {
this.searchDataList.mediaBigCategory = []
if(模块分类数据存在时) {
req.get(模块分类).then(res => {
this.searchDataList.mediaBigCategory = res
this.$forceUpdate()
})
}
}
这次惊奇的发现成功了!之前遇到这种渲染问题都是这三个办法依次试试,这次就来找找原因出在哪。
查找原因
首先仔细看看官方文档对于这三个方法的解答。
首先是this.$set()
可以看出该方法是在响应对象里面添加一个响应对象,并且触发视图更新,看似跟我们需求很符合,但是看看我们的代码发现,我们在最开始的使用为了让他请清空而写了一行代码:this.searchDataList.mediaBigCategory = []
,也就是说在我们使用this.$set(this.searchDataList, 'mediaBigCategory', res)
的时候,searchDataList属性里面已经存在了mediaBigCategory属性,并且官方说了普通方法添加属性是无法被检测,这就是this.$set()失效的原因。想到这里我们应该已经知道怎么修改了
修改后代码
getChildMedia() {
this.$set(this.searchDataList, 'mediaBigCategory ', res)
this.searchDataList.mediaBigCategory = []
if(模块分类数据存在时) {
req.get(模块分类).then(res => {
this.searchDataList.mediaBigCategory = res
})
}
}
修改完成后,发现已经可以进行数据渲染。
既然$set可以,那$nextTick呢?
仔细阅读官方解释可以明白,这个方法会将你的操作放进一个回调函数里面,然后等DOM更新完成,执行回调函数。那么我们使用普通方法this.searchDataList.mediaBigCategory = []
添加的属性是不算响应属性的,也就不会进行DOM更新,所以无论怎样,数据都不会进行渲染。
该方法主要是避免vue的覆盖机制:例如当一个属性被连续修改多次,vue是不会多次渲染的,为了节省性能,会只渲染最后一次,而nextTick就可以让你在渲染多次的过程中进行一些操作。
既然$nextTick不行,那$forceUpdate为什么可以呢?
咱们同样从官方文档里找答案。该方法会使整个vue实例重新渲染!也就是说整个页面相当于重新渲染过,而渲染时,数据是不会丢失的,就相当于重新渲染的时候mediaBigCategory 属性最开始就存在于searchDataList对象里,数据自然也就会被渲染出来。
注意的时,虽然使用forceUpdate可以将数据渲染出来,但并不会将非响应属性改为响应属性,也就是说你修改mediaBigCategory 属性依旧不会进行重新渲染,必须再次进行forceUpdate
补充
除了上面的几个方法外还有一个更简单的方法,就是给data里面的对象里面的属性默认值。例如
searchDataList: {
mediaBigCategory: []
}
这样在数据一开始进行渲染就会自动将mediaBigCategory渲染成响应属性,自然修改的时候也就会跟着渲染。
总结
说到最后,还是官方文档解决了所有问题!虽然每个方法就一两句话,但绝对不多一句废话,不少一句要点。官方文档,yyds!