我们先看一下dcloud官网上大家使用uni-app开发过程中针对uni-ui扩展组件 uni-rate 评分组件的反馈:
很多人在使用uni-rate组件时都碰到类似的问题,可以看出无论是官网上还是各种平台上都是挺多反馈的,接下来我们探究下根本原因及解决方法:
可能很多人都误解了dcloud,uni-rate组件的研发人员。
造成这种现象的根本原因是:vue的单向数据流决定的。所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。那么我们要解决这个问题,就是我们需要对prop 进行"双向绑定"(注意:该双向绑定不是真正的双向绑定,文字中会有解释),并且能够明确应用的数据流向。
子组件添加监听事件,父组件使用.sync修饰符配合实现父子组件传值对prop 进行"双向绑定"。
val: 2
watch:{
valueSync: {
handler(val) {
this.$emit('update:value', val);
}
}
},
官方提供有uniRate 的 value 改变时触发的change事件,其返回参数包含当前评分信息。使用方法如下:
onChange(e) {
// console.log('rate发生改变,当前评分为:' + e.value)
}
场景一:
我们一个页面中只需要使用一次评分组件。当前评分使用change事件也不比双向绑定更新麻烦多少。
场景二:
我们一个页面使用多次评分组件。
这时候如果我们想要去对应获取已更新的多个评分组件的当前评分值,第一点我们就需要考虑,当前change事件是哪个评分组件当前评分修改时触发的?
针对这个问题,假设我们使用change方法去做,那么总的思想都是解决如何定位当前操作的评分组件的问题,以此思想提供以下两种具有代表性的解决方式:
1.每个组件声明对应的change方法(如:onChange、onChange1、onChange2.....)。这种方法原则上是不推荐的,毕竟声明那么多同样功能的方法,不需要也大可不必。
2.我们可以添加一个属性rateId作为各评分组件的唯一标识,在change事件返回参数中返回该标识。具体使用:
rateId: {
//评分组件唯一标识
type: [Number, String],
default: 0
},
this.$emit("change", {
id: this.rateId,
value: this.valueSync
});
onChange(e) {
//console.log(e.id);
}
感觉第二种方式体验也还不错,同样官方没有提供该属性,如果采取这种方式,官方也同样需要对应修改代码。但是我们经常用vue的小伙伴们就会考虑到一个问题,如果我每个组件的当前评分值能够实现"双向绑定"就好了,我们直接绑定一个值,修改当前评分时,该值对应更新,多方便。确实,我也这么觉得...,在官方实现该功能的理想状态下,我们只需要如下操作就好:
data() {
return {
val: 2
}
}
看上去确实方便了很多,这也就是为什么要写这篇文章的一个初衷。
单向数据流
但通过网上大家给出的解决该问题的各种方法,我发现很多使用vue的前端小伙伴们都不太了解vue的单向数据流。这也是我写该篇文章的另一个初衷。接下来我们就来了解下究竟什么是单向数据流以及为什么说上面我们说的“双向绑定”不是真正的双向绑定。
vue官方给的描述是这样的:
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
我们有两种常见的试图变更一个 prop 的情形:
1.这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
这也就是为什么有的小伙伴提到的子组件中为什么不直接改变value值,还要用一个valueSync的其中一个原因。
2.这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
根据注意中提到的,那我们就还有一种解决当前评分实时更新的方法,如下:
obj:{
type: Object,
default: _=>{
value: 0
}
}
this.valueSync = Number(this.obj.value);
_onClick(index) {
if (this.disabled) {
return;
}
this.valueSync = index + 1;
this.obj.value = index + 1;
this.$emit("change", {
value: this.valueSync
});
}
上述方法同样可以实现当前评分实时更新,但是我们不推荐使用这种方式。
至此,针对uni-app的uni-ui的评分组件当前评分无法实时更新问题,详细分析及解决问题的方法基本已经描述完毕。如本篇文章中的拙见有冒犯到各位,欢迎私信,本人一定接受建议并及时改正。当然,也欢迎和我沟通交流更好的解决方法。